Search code examples
reactjsreact-nativenavigationreact-hooksreact-navigation-stack

React Native Hooks and Authentication flow


I am currently setting up my authentification flow for my React Native app and I ve got the following issues.

My navigation screen looks like that


export default CreateStack = () => {
  const [isLoading, setIsLoading] = React.useState(true);
  const [userToken, setUserToken] = React.useState(null);

  const authContext = React.useMemo(() => {
    return {
      signIn: () => {
        setIsLoading(false);
        setUserToken('asdf');
      },
      signUp: () => {
        setIsLoading(false);
        setUserToken('asdf');
      },
      signOut: () => {
        setIsLoading(false);
        setUserToken(null);
      },
    };
  }, []);

  React.useEffect(() => {
    setTimeout(() => {
      setIsLoading(false);
    }, 1000);
  }, []);

  if (isLoading) {
    return <LoadingScreen />;
  }
  return (
    <AuthContext.Provider value={authContext}>
      <NavigationContainer>
        {userToken ? (
          <Stack.Navigator
            screenOptions={{headerShown: false}}
            initialRouteName={Browser}>
            <Stack.Screen name="Browser" component={TabsScreen} />
            <Stack.Screen name="PreScreen" component={PreScreen} />
            <Stack.Screen name="Player" component={Player} />
          </Stack.Navigator>
        ) : (
          <AuthStack.Navigator
            screenOptions={{headerShown: false}}
            initialRouteName={RegisterLogin}>
            <AuthStack.Screen name="RegisterLogin" component={RegisterLogin} />
            <AuthStack.Screen name="Login" component={Login} />
            <AuthStack.Screen name="Register" component={Register} />
          </AuthStack.Navigator>
        )}
      </NavigationContainer>
    </AuthContext.Provider>
  );
};

AuthContext :

import React from 'react';

export const AuthContext = React.createContext();

And when i am trying to access the singIn function from my navigation screen from my Log in screen i am getting the error for invalid hook call.

My log in screen is formed from a class as following

class Login extends Component {
  state = {
    email: VALID_EMAIL,
    password: VALID_PASSWORD,
    errors: [],
    loading: false,
  };

  handleLogin() {
    const {signIn} = React.useContext(AuthContext);
    const {navigation} = this.props;
    const {email, password} = this.state;
    let errors = [];

    Keyboard.dismiss();
    this.setState({loading: true});

    if (email !== VALID_EMAIL) {
      errors.push('email');
    }
    if (password !== VALID_PASSWORD) {
      errors.push('password');
    }

    this.setState({errors, loading: false});

    if (!errors.length) {
      signIn();
    }
  }
  render() {
    const {navigation} = this.props;
    const {loading, errors} = this.state;
    const hasErrors = (key) => (errors.includes(key) ? styles.hasErrors : null);
    return (
      <View style={styles.mainContainerView}>
        <StatusBar barStyle="light-content" />
        <NavigationBarDown title="LogIn" />
        <TextInput
          style={[styles.TextInput, hasErrors('email')]}
          label="Email"
          error={hasErrors('email')}
          placeholder="Email Adress"
          autoCapitalize="none"
          autoCorrect={false}
          onChangeText={(text) => this.setState({email: text})}
        />
        <TextInput
          label="Password"
          placeholder="Password"
          error={hasErrors('password')}
          style={[styles.TextInput, hasErrors('password')]}
          onChangeText={(text) => this.setState({password: text})}
        />
        <TouchableOpacity
          style={styles.singInButton}
          gradient
          onPress={() => this.handleLogin()}>
          {loading ? (
            <ActivityIndicator size="small" color="white" />
          ) : (
            <Text style={styles.logInText}>Login</Text>
          )}
        </TouchableOpacity>

        <Text>Dont have an account ?</Text>
        <TouchableOpacity>
          <Text>Register here in here</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

export default Login;

I am stuck here for a few days so any help is welcomed

After some updates i came out with this

import React, {useContext} from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StatusBar,
  Keyboard,
  KeyboardAvoidingView,
} from 'react-native';
import styles from './style';
import NavigationBarDown from '../../components/NavigationBardown/NavigationBarDown';
import {AuthContext} from '../../components/Navigation/context';
import login from './action';

const VALID_EMAIL = 'c';
const VALID_PASSWORD = '2';
const EMPTY = '';

class Login extends React.Component {
  state = {
    email: VALID_EMAIL,
    password: VALID_PASSWORD,
    errors: [],
    loading: false,
  };

  handleLogin = () => {
    const {navigation} = this.props;
    const {email, password} = this.state;
    let errors = [];

    Keyboard.dismiss();
    this.setState({loading: true});

    if (email !== VALID_EMAIL) {
      errors.push('email');
    }
    if (password !== VALID_PASSWORD) {
      errors.push('password');
    }

    this.setState({errors, loading: false});

    if (!errors.length) {
      this.props.signIn;
    }
  };
  render() {
    const {navigation} = this.props;
    const {loading, errors} = this.state;
    const hasErrors = (key) => (errors.includes(key) ? styles.hasErrors : null);
    return (
      <View style={styles.mainContainerView}>
        <StatusBar barStyle="light-content" />
        <NavigationBarDown title="LogIn" />
        <TextInput
          style={[styles.TextInput, hasErrors('email')]}
          label="Email"
          error={hasErrors('email')}
          placeholder="Email Adress"
          autoCapitalize="none"
          autoCorrect={false}
          onChangeText={(text) => this.setState({email: text})}
        />
        <TextInput
          label="Password"
          placeholder="Password"
          error={hasErrors('password')}
          style={[styles.TextInput, hasErrors('password')]}
          onChangeText={(text) => this.setState({password: text})}
        />
        <TouchableOpacity
          style={styles.singInButton}
          gradient
          onPress={() => this.handleLogin()}>
          {loading ? (
            <ActivityIndicator size="small" color="white" />
          ) : (
            <Text style={styles.logInText}>Login</Text>
          )}
        </TouchableOpacity>

        <Text>Dont have an account ?</Text>
        <TouchableOpacity>
          <Text>Register here in here</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

const LoginWithContext = (props) => {
  const {signIn} = useContext(AuthContext);
  return <Login signIn={signIn} />;
};

export default LoginWithContext;

but now still is not working


Solution

  • import React, { useContext } from 'react'
    import AuthContext from '../path/to/context'
    
    class Login extends React.Component {
      handleLogin = () => {
        const { signIn } = this.props
        const {navigation} = this.props;
        const {email, password} = this.state;
        
        keyboard.dismiss()
        
        let errors = []
        
        if (email !== VALID_EMAIL) {
          errors.push('email');
        }
        if (password !== VALID_PASSWORD) {
          errors.push('password');
        }
        
        if (errors.length) {
          this.setState({ errors })
        }
        else {
          const callback = () => {
            this.setState({ loading: false })
          }
          
          this.setState(prevState => { 
            signIn()
            return { ...prevState, loading: true }
          }), callback)
        }
      }
    }
    
    const LoginWithContext = props => {
      const { signIn } = useContext(AuthContext)
      return (
        <Login signIn={signIn} />
      )
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>