0
votes

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 />
 );
}
3
You may want to check if your Login component is properly hook in the react-navigation stack? And please don't directly make changes of your state this.state.login = form;, use this.setState instead - Ewe Tek Min
@EweTekMin Thanks for the advice! as I've mentioned before, I'm very new to React Native and react-navigation, since the Login component is the very first screen in my app, how to hook it into the navigation stack? - Jack
Please check on my answer below, thanks - Ewe Tek Min

3 Answers

1
votes

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.

1
votes

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. :)

0
votes

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.