Blackol
Blackol

Reputation: 5

TypeError: undefined is not an object (evaluating '_this.props.navigation.navigate') reactNavigation 5

I'm trying to change the view from my search.js file. but I can't and I encounter this error in my app that crashes. And every time I do a console.log(this.props) in my search.js file I get this result : ------------------------------------opooi- Object {} . <code>enter image description here</code>

This is my Search.js file

//Components/Search.js

import React from 'react'
import { StyleSheet, View, TextInput, Button, Text, FlatList, ActivityIndicator } from 'react-native'
import FilmItem from './FilmItems'
import { getFilmsFromApiWithSearchedText } from '../API/TMDBApi'

class Search extends React.Component {



  _displayDetailForFilm = (idFilm) => {
    //console.log("Display film with id " + idFilm + this )
    console.log('----------------------opooi-')
   console.log(this.props)
    // Cette commande ci-dessous est non fonctionnel donc si vous avez la solution...
    this.props.navigation.navigate("Details")
  }

  constructor(props) {
    super(props)
    this.page = 0
    this.totalPages = 0
    this.searchedText = "" // Initialisation de notre donnée searchedText en dehors du state
    this.state = {
      films: [],
      isLoading : false // Affiche temoin de chargement desactiver de base
    }
  }

  _loadFilms() {
    this.setState({ isLoading: true})
    if (this.searchedText.length > 0) { // Seulement si le texte recherché n'est pas vide
      getFilmsFromApiWithSearchedText(this.searchedText, this.page+1).then(data => {
        this.page = data.page
        this.totalPages = data.total_pages
        this.setState({ 
            films: [...this.state.films , ...data.results ],
          isLoading : false 
        })
      })
    }
  }

  _displayLoading() {
    if (this.state.isLoading) {
      return (
        <View style={styles.loading_container}>
          <ActivityIndicator size='large' />
          {/* Le component ActivityIndicator possède une propriété size pour définir la taille du visuel de chargement : small ou large. Par défaut size vaut small, on met donc large pour que le chargement soit bien visible */}
        </View>
      )
    }
  }

_searchFilms() {
  this.page = 0
  this.totalPages = 0
  this.setState({
    films: []
  }, ()=> {
    // J'utilise la paramètre length sur mon tableau de films pour vérifier qu'il y a bien 0 film
  console.log("Page : " + this.page + " / TotalPages : " + this.totalPages + " / Nombre de films : " + this.state.films.length)
  this._loadFilms()
  })

}

  _searchTextInputChanged(text) {
    this.searchedText = text // Modification du texte recherché à chaque saisie de texte, sans passer par le setState comme avant
  }

  render() {
    const { film, displayDetailForFilm } = this.props
    console.log('----------------------------')
    console.log(this.props);

    // test si lóbject this.props est null..
    return (
        <View
            style={styles.main_container}
            onPress={() => displayDetailForFilm(film.id)}
          > 
        <TextInput
          style={styles.textinput}
          placeholder='Titre du film'
          onChangeText={(text) => this._searchTextInputChanged(text)}
          onSubmitEditing={() => this._searchFilms()}
        />
        <Button title='Rechercher' onPress={() => this._searchFilms()}/>
        <FlatList
          data={this.state.films}
          keyExtractor={(item) => item.id.toString()}

          onEndReachedThreshold={0.5}
          onEndReached={() => {
            if (this.page < this.totalPages) {
              this._loadFilms()
            }
          }}

This is my Navigation.js file

// Navigation/Navigation.js
import * as React from 'react';
import Search from '../Components/Search';
import FilmDetail from '../Components/FilmDetail';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();



const HomeScreen =({ navigation }) => {
  return (

    <Search/>
  );
}



const DetailsScreen = ({ navigation }) => {
  return (
    <FilmDetail/>
  );
}



function Navigation() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Home' }} />
        <Stack.Screen name="Details" component={DetailsScreen} options={{ title: 'Details' }} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}
export default Navigation;

Here is my Filmitem.js file where I use TouchableOpacity

// Components/FilmItem.js

import React from 'react'
import { StyleSheet, View, Text, Image, TouchableOpacity } from 'react-native'
import { getImageFromApi } from '../API/TMDBApi'

class FilmItem extends React.Component {
  render() {
    const film = this.props.film
    const displayDetailForFilm = this.props.displayDetailForFilm
    return (
      <TouchableOpacity
        style={styles.main_container}
        onPress={() => displayDetailForFilm(film.id)}>
        <Image
          style={styles.image}
          source={{uri: getImageFromApi(film.poster_path)}}
        />
        <View style={styles.content_container}>
          <View style={styles.header_container}>
            <Text style={styles.title_text}>{film.title}</Text>
            <Text style={styles.vote_text}>{film.vote_average}</Text>
          </View>
          <View style={styles.description_container}>
            <Text style={styles.description_text} numberOfLines={6}>{film.overview}</Text>
            {/* La propriété numberOfLines permet de couper un texte si celui-ci est trop long, il suffit de définir un nombre maximum de ligne */}
          </View>
          <View style={styles.date_container}>
            <Text style={styles.date_text}>{film.release_date}</Text>
          </View>
        </View>
      </TouchableOpacity>
    )
  }
}

const styles = StyleSheet.create({
  main_container: {
    height: 190,
    flexDirection: 'row'
  },
  image: {
    width: 120,
    height: 180,
    margin: 5,
    backgroundColor: 'gray'
  },
  content_container: {
    flex: 1,
    margin: 5
  },
  header_container: {
    flex: 3,
    flexDirection: 'row'
  },
  title_text: {
    fontWeight: 'bold',
    fontSize: 20,
    flex: 1,
    flexWrap: 'wrap',
    paddingRight: 5,
    color: '#FFFFFF'
  },
  vote_text: {
    fontWeight: 'bold',
    fontSize: 26,
    color: '#FFFFFF'
  },
  description_container: {
    flex: 7
  },
  description_text: {
    fontStyle: 'italic',
    color: '#FFFFFF'
  },
  date_container: {
    flex: 1
  },
  date_text: {
    textAlign: 'right',
    fontSize: 14,
    color: '#FFFFFF'
  }
})

export default FilmItem

Here's the view I'm trying to access

// Components/FilmDetail.js

import React from 'react'
import { StyleSheet, View, Text } from 'react-native'

class FilmDetail extends React.Component {
  render() {
    return (
      <View style={styles.main_container}>
        <Text>Détail du film</Text>

        <Button
      title={`Go to ${Home}`}
      onPress={() => navigation.navigate(Home)}
    />
      </View>
    )
  }
}

const styles = StyleSheet.create({
  main_container: {
    flex: 1,
  }
})

export default FilmDetail

Upvotes: 0

Views: 775

Answers (1)

Jacob
Jacob

Reputation: 78840

The error tells you what you need to know

Undefined is not an object (evaluating '_this.props.navigation.navigate')

It's referring to this line.

this.props.navigation.navigate("Details")

Obviously this.props is an object, but this.props.navigation is not, so this.props.navigation.navigate will fail to evaluate. The reason why this.props.navigation is undefined is because you didn't pass a navigation prop to your component.

const HomeScreen =({ navigation }) => {
  return (
    <Search/>
  );
}

Here you've defined a HomeScreen component that is receiving a navigation prop, but the error you're getting is in your Search component which does not have that prop. You can simply pass it in:

const HomeScreen =({ navigation }) => {
  return (
    <Search navigation={navigation}/>
  );
}

...or if your component is really that simple, you can do:

const HomeScreen = Search;

...and let Stack.Navigator pass the navigation prop directly into your Search component.

Upvotes: 1

Related Questions