react-navigation / react-navigation

Routing and navigation for your React Native apps
https://reactnavigation.org
23.54k stars 5.02k forks source link

undefined is not an object ('this.props.navigation.navigate') error #3719

Closed ozican closed 6 years ago

ozican commented 6 years ago

Hello people, I use react native expo, when I am trying to take this.props.navigation can not find. I think this is a huge bug. I am trying to take it from a listview and I think props.navigation don't come because page don't come... Could I explain? Because when I try same codes but full secreen the component which is in the listview, it works. I can log to this.props.navigation....

Here is my code,

This is part of TodoList.js

renderRow(todos) {
  return <TodoDetail todos={todos} />;
}
renderRow2(todos) {
  return <TodoDetailChecked todos={todos} />;
}
render() {
  // const { navigate } = this.props.navigation;      

  const { container, inputContainer, inputText } = styles;
 if (!this.state.screenSize) {
  return (
    <View style={container} >
        <View style={inputContainer} >
        <Icon name={'add'} />
        <TextInput
            style={inputText}
            underlineColorAndroid='transparent'
            placeholder="Yapılacak iş ekle..."
            placeholderTextColor="#FFFFFF"
            value={this.props.todo}            
            onChangeText={todo => this.props.todoChanged(todo)}
.............................
.............................
..........................
    <ListView
            enableEmptySections
            dataSource={this.dataSource}
            renderRow={this.renderRow}
    />
    </View>    
    <View style={{ flex: 1 }}>
    <View style={{ height: 1, backgroundColor: 'gray' }} />    
    <ListView
            enableEmptySections
            dataSource={this.dataSource}
            renderRow={this.renderRow2}
    />

This is part of TodoDetail.js

  clickText() {
    const { todo } = this.props.todos;   
    // const { navigate } = this.props.navigation; 
    return (
      <TouchableOpacity onPress={this.seeDetail.bind(this)} >
        <Text numberOfLines={1}> {todo} </Text>
      </TouchableOpacity>
    );
  }
  seeDetail() {
    const { navigate } = this.props.navigation;
    navigate("Profile", { name: "Jane" });
    console.log('click');
  }
brentvatne commented 6 years ago

hi there! sorry you're having an issue. as the issue template explains, we require that you provide a runnable example that reproduces the bug on https://snack.expo.io or in a github repository. please try to minimize the superfluous code and focus only on reproducing the bug.

you might wonder, why are we strict about this? the reason is that there are only a small number of maintainers on this project and there are hundreds of people who report issues. if it takes us 20 minutes on average to reproduce each issue and we go through 10 issues each day, that's about half of our day just trying to reproduce bugs, without even beginning to investigate solutions. compare that to 20 minutes for each person who reports an issue.

please create a new issue with this and i'd be happy to review it!

spaceforestchu commented 6 years ago

Hi,

I don't know if this will help you, but this is how I solved. I passed navigation as props in Posts that's where my initial component when the screen loads.

App.js

screen shot 2018-04-13 at 5 18 06 pm

Home.js

screen shot 2018-04-13 at 5 17 27 pm screen shot 2018-04-13 at 5 19 57 pm

Basically, I passed this.props.navigation to the Home component and it worked. I think it is because Home component is where it loads first.

I hope it helps.

firllyw commented 5 years ago

@spaceforestchu

i still have this issue, simply i want to change page when a user click on my <TouchableNativeFeedback> on my NewsDetail.js. i setup my StackNavigator on my index.android.js (i don't have any App.js) below is my index.android.js,

export default class App extends React.Component{
    render() {
        return (
            <View style={{flex:1}}>
                <Header title={'Today Headline'}/>
                <NewsList/>
            </View>
        );
    }
}

const Navigator = createStackNavigator({
    NewsListScreen: {
        screen: App
    },
    NewsDetailScreen: {
        screen: NewsDetail
    },
    ReadDetailScreen: {
        screen: ReadNews,
    },
});

const AppContainer = createAppContainer(Navigator);
AppRegistry.registerComponent(appName, () => App);

Now, i try to call another screen if a user press on TouchableNativeFeedback on my NewsDetail.js, below is my code for NewsDetail.js

export default class NewsDetail extends React.Component {
    render() {
        console.log(this.props);
        const {author, title, urlToImage, source} = this.props.news;
        const {textStyle, sourceText} = style;
        return (
            <TouchableNativeFeedback onPress={this.articleView}>
                <RkCard rkType='story' key={title}>
                    <Image rkCardImg source={{uri: urlToImage}}/>
                    <View rkCardContent>
                        <Text style={sourceText}> {source.name}</Text>
                        <Text style={textStyle}> {title}</Text>
                    </View>
                    <View rkCardFooter>
                        <Text>{author}</Text>
                    </View>
                </RkCard>
            </TouchableNativeFeedback>
        );
    }

    articleView = () => {
        this.props.navigation.navigate('ReadNews');
    }

}

notice on index.android.js, i call my NewsDetail via NewsList.js. i tried to use this.props.navigation.navigate on articleView function on NewsDetail but it returns cannot read property navigate of undefined, when i log this.props, indeed there isn't any navigate object. How do i fix it? here's my NewsList.js

export default class NewsList extends React.Component {
    constructor(props) {
        super(props);
    }
    state = {news: []};

    componentWillMount() {
        axios.get(API.Headline)
            .then(response => this.setState({news: response.data.articles}));
    }

    renderNews() {
        return this.state.news.map(topic =>
            <NewsDetail key={topic.title} news={topic}/>
        )
    }

    render() {
        return (
            <ScrollView>
                {this.renderNews()}
            </ScrollView>
        );
    }

i assume there is something wrong in my index.android.js but i can't figure it out. in desperate need of any suggestions. Thanks in advance

bilobom commented 5 years ago

@firllyw it should be:

articleView = () => {
        this.props.navigation.navigate('ReadDetailScreen');
    }

as per createStackNavigator docs,

createStackNavigator(RouteConfigs, StackNavigatorConfig);

The route configs object is a mapping from route name to a route config, which tells the navigator what to present for that route.

For the this.props.navigation.navigate's undefined

the docs says, as bellow commented ,that

// When ProfileScreen is loaded by the StackNavigator, it will be given a navigation prop.

this means that the screen your calling the this.props.navigation.navigate('declaredScreen') must be declared in the routeConfigs for it to receive navigation props.

createStackNavigator({
  // For each screen that you can navigate to, create a new entry like this:
  Profile: {
    // `ProfileScreen` is a React component that will be the main content of the screen.
    screen: ProfileScreen,
    // When `ProfileScreen` is loaded by the StackNavigator, it will be given a `navigation` prop.

    // Optional: When deep linking or using react-navigation in a web app, this path is used:
    path: 'people/:name',
    // The action and route params are extracted from the path.

    // Optional: Override the `navigationOptions` for the screen
    navigationOptions: ({ navigation }) => ({
      title: `${navigation.state.params.name}'s Profile'`,
    }),
  },

  ...MyOtherRoutes,
});

If your calling the navigation.navigate props from a child of a declaredScreen parent, just pass navigation to him like the following:

const Navigator = createStackNavigator({
    DeclaredScreenParent: {
        screen: DeclaredScreenParent 
    },
export default class DeclaredScreenParent extends React.Component{
    render() {
        return (
            <View>
                <ScreenChild navigation={this.props.navigation}/>
            </View>
        );
    }
}
});
class ScreenChild extends React.Component{
    componentDidUpdate(){
      console.log(this.props.navigation)   // -------> something other than undefined
    }
    render() {
        return (
            <View>
                <Text> 'I'm Screen child, I don't have navigation props, but it will be passed down by my parent '</Text>
            </View>
        );
    }
}
});
Vikmanatus commented 5 years ago

Hi,

I don't know if this will help you, but this is how I solved. I passed navigation as props in Posts that's where my initial component when the screen loads.

App.js

screen shot 2018-04-13 at 5 18 06 pm

Home.js

screen shot 2018-04-13 at 5 17 27 pm screen shot 2018-04-13 at 5 19 57 pm

Basically, I passed this.props.navigation to the Home component and it worked. I think it is because Home component is where it loads first.

I hope it helps.

Thank you very much sir, you saved my afternoon ! I wish you a great day ! :)

JarnoNijhof commented 4 years ago

tnx, I spend a log time trying to solve this error... It's so confusing because it works but it gives an error.

easily solvable by passing the props like this in the screen where you want to navigate;

const SettingsScreen = props => {
    return (
        <ScreenContainer {...props} menu title="Settings">
            <SettingsForm {...props} />
        </ScreenContainer>
    )
}

export default SettingsScreen
mohammadsakhidel commented 3 years ago

I finally solved the problem with NavigationContext. I explained my way in a post at stackoverflow.com, this is the link:

https://stackoverflow.com/questions/56890132/this-props-in-component-does-not-contain-navigation-property/65275091#65275091

ziggybaba1 commented 3 years ago

Hello people, I use react native expo, when I am trying to take this.props.navigation can not find. I think this is a huge bug. I am trying to take it from a listview and I think props.navigation don't come because page don't come... Could I explain? Because when I try same codes but full secreen the component which is in the listview, it works. I can log to this.props.navigation....

Here is my code,

This is part of TodoList.js

renderRow(todos) {
  return <TodoDetail todos={todos} />;
}
renderRow2(todos) {
  return <TodoDetailChecked todos={todos} />;
}
render() {
  // const { navigate } = this.props.navigation;      

  const { container, inputContainer, inputText } = styles;
 if (!this.state.screenSize) {
  return (
    <View style={container} >
        <View style={inputContainer} >
        <Icon name={'add'} />
        <TextInput
            style={inputText}
            underlineColorAndroid='transparent'
            placeholder="Yapılacak iş ekle..."
            placeholderTextColor="#FFFFFF"
            value={this.props.todo}            
            onChangeText={todo => this.props.todoChanged(todo)}
.............................
.............................
..........................
    <ListView
            enableEmptySections
            dataSource={this.dataSource}
            renderRow={this.renderRow}
    />
    </View>    
    <View style={{ flex: 1 }}>
    <View style={{ height: 1, backgroundColor: 'gray' }} />    
    <ListView
            enableEmptySections
            dataSource={this.dataSource}
            renderRow={this.renderRow2}
    />

This is part of TodoDetail.js

  clickText() {
    const { todo } = this.props.todos;   
    // const { navigate } = this.props.navigation; 
    return (
      <TouchableOpacity onPress={this.seeDetail.bind(this)} >
        <Text numberOfLines={1}> {todo} </Text>
      </TouchableOpacity>
    );
  }
  seeDetail() {
    const { navigate } = this.props.navigation;
    navigate("Profile", { name: "Jane" });
    console.log('click');
  }

I suspect your issue is due to calling const { navigate } = this.props.navigation; in a sub screen component that doesn't go through navigation. If that is the case then you need to pass a method from the sub screen component then to the parent where navigation exist. Parent: ` childNavigate=()=>{ const {navigate}=this.props.navigation; navigate("Screen"); } render() { return (

); } ` Child Page (Component):

`render() { return (

this.props.onNavigate()} />

); }`

greenskin86 commented 3 years ago

For those who come here after a while: This issue may be caused with one more thing, which is not described anywhere (at least I couldn't find it).

I'm using class components. When this error occured, I didn't notice that my function was declared as:

navigateBack() {
   this.props.navigation.dispatch(...);
}

This is what worked for me:

navigateBack = () => {
   this.props.navigation.dispatch(...);
}
github-actions[bot] commented 3 years ago

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.