Jack
Jack

Reputation: 182

How to fix "Cannot read property 'navigate'of undefined"

I'm very new to React Native, right now I'm trying to build a Login component, once login succeed, I'd like to redirect the component from Login screen to the main screen. I'm using React Navigation and somehow it keeps showing me the error "Cannot read property 'navigate' of undefined".

Did I do something wrong or I just missed something? Very appreciate for any kind help!

I'm using Visual Studio Code-Insiders (v1.37.0) npm -v:6.4.1 expo -V:2.21.2 react-navigation: ^3.11.0

The code very basic:

import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Avatar, Button, Icon } from 'react-native-elements'
import * as t from 'tcomb-form-native'
import Router from '../config/router';

const Form = t.form.Form;
const PWD = '2';
const UME = 'J';

class Login extends Component {

constructor(props) {
    super(props);

    this.state = {
        login: {}
    };

    this.LoginModel = t.struct({
        un: t.String,
        pwd: t.String,
        rm: t.Boolean
    });

    this.LoginOptions = {
        fields: {
            un: {
                label: 'User Name',
                error: 'Please type your username'
            },
            pwd: {
                label: 'Password',
                password: true,
                secureTextEntry: true,
                error: 'Please type the password'
            },
            rm: {
                label: 'Remember me'
            }
        }
    }

}

onChange(form) {
    this.state.login = form;
}

onSubmit() {

    console.log(this.props); // will output an empty object! but how?

    const { navigate } = this.props.navigation; // Here is where the error message captured and throw out!

    const value = this.refs.form.getValue(); 

    if (value.un === UME && value.pwd === PWD) {
        alert('Welcome back, Jack');
        // setTimeout(() => {
        //     navigate('Home');
        // }, 300);
    }
}

render() {
    return (
        <View style={styles.container}>

            <View style={{ alignItems: "center", marginBottom: 30 }}>
                <Avatar
                    size="xlarge"
                    icon={{ name: 'apple', color: 'black', type: 'font-awesome' }}
                    overlayContainerStyle={{ backgroundColor: 'white' }}
                    onPress={() => console.log("Works!")}
                    activeOpacity={0.7}
                    containerStyle={{ marginTop: 15 }}
                />
            </View>

            <Form
                ref="form"
                type={this.LoginModel}
                value={this.state.login}
                options={this.LoginOptions}
                onChange={(f) => this.onChange(f)}
            />

            <Button
                icon={
                    <Icon
                        type='font-awesome'
                        name="user"
                        size={25}
                        color="white"
                        iconStyle={styles.icon}
                    />
                }
                title="Login"
                style={styles.btn}
                onPress={()=>this.onSubmit()}
            />

        </View>
    );
}
}

const styles = StyleSheet.create({
container: {
    flex: 1,
    padding: 80
},
icon: {
    paddingRight: 10
},
btn: {

}
});

export default Login;

UPDATE: This is the App.js

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import LoginScreen from './src/screens/Login'

export default function App() {
  return (
    <LoginScreen />
 );
}

Upvotes: 0

Views: 2086

Answers (3)

Marcelo Monteiro
Marcelo Monteiro

Reputation: 263

Using a arrow function might work:

onSubmit = () => {
  console.log(this.props);
  const { navigate } = this.props.navigation;
//...
}

The keyword 'this' refers to the function that calls it. But onSubmit has no props. "ES5 introduced the bind() method to set the value of a function this regardless of how it's called, and ES2015 introduced arrow functions which don't provide their own this binding (it retains the this value of the enclosing lexical context)." You can read more about that in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

Always prefer using arrow functions to avoid this. :)

Upvotes: 1

Ewe Tek Min
Ewe Tek Min

Reputation: 865

Based on your code, you have yet created the navigation stack, just add a StackNavigator for your Login component should work

// Other imports
import { createStackNavigator, createAppContainer } from "react-navigation";

...

class Login extends Component {
 ....
}

const styles = StyleSheet.create({...});

const AppNavigator = createStackNavigator({
  Login: {
    screen: Login
  }
});

export default createAppContainer(AppNavigator);

You can refer the react-navigation's document

However this is just a quick way of doing it, you may need to structure the navigation stack in separate file.

Upvotes: 1

Auticcat
Auticcat

Reputation: 4489

From the code i would assume that your are rendering LoginScreen inside your App.js. Doing that, you are not putting LoginScreen inside any navigator, making this.props.navigation equal to undefined.

Upvotes: 0

Related Questions