I am new to react native and I tried to implement a simple login with navigation between login and home screen using React Navigation. However I cannot figure out how to make user goes to home when previous user creds is stored in asyncStorage and goes to login screen when no creds stored in asyncStorage. I keep getting into login screen even if I'm signed in. I've tried to search, copy, modify any example I found on the internet but none of them meets my need, not to mention every example uses react navigation version 4.xx and below. How can I achieve this, so I can implement them for every other code in my react native project. Maybe I'm missing something or write something incorrectly? Any help appreciated. Also the splash screen setTimeOut seems not working.
The flow should be:
Splash Screen -> isLogin? -> Home
Splash Screen -> is not login? -> login screen
but I keep getting
Splash Screen (in a blink) -> login screen
no matter whether I'm logged in or not
Here is my App.js
import 'react-native-gesture-handler';
import React, {Component, useEffect, useReducer} from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import {loadUser} from './src/util/userStorage';
import LoginFunct from './src/loginFunct';
import Home from './src/home';
import SplashScreen from './src/splashScreen';
const Stack = createStackNavigator();
function App({navigation}) {
const [state, dispatch] = useReducer(
(prevState, action) => {
switch (action.type) {
case 'FETCH_CREDS':
return {
...prevState,
user: action.user,
loading: false,
isLogin: action.isLogin,
};
}
},
{
loading: true,
isLogin: false,
user: {},
},
);
useEffect(() => {
const fetchData = () => {
let userData;
let loggedIn;
try {
// setTimeout(() => {
loadUser().then((data) => {
if (data) {
console.log(data);
userData = data;
loggedIn = true;
}
});
// }, 2000);
} catch (e) {
console.log(e);
}
dispatch({
type: 'FETCH_CREDS',
user: userData,
loading: false,
isLogin: loggedIn,
});
};
fetchData();
}, []);
return (
<NavigationContainer>
<Stack.Navigator>
{!state.loading ? (
state.isLogin ? (
<>
{console.log(state.isLogin)}
<Stack.Screen
name="Home"
component={Home}
options={{
title: 'Home',
}}
/>
<Stack.Screen
name="LoginFunct"
component={LoginFunct}
options={{
title: 'Sign In',
}}
/>
</>
) : (
<>
{console.log(state.isLogin)}
<Stack.Screen
name="LoginFunct"
component={LoginFunct}
options={{
title: 'Sign In',
}}
/>
<Stack.Screen
name="Home"
component={Home}
options={{
title: 'Home',
}}
/>
</>
)
) : (
<Stack.Screen name="SplashScreen" component={SplashScreen} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Here is my userStorage where the loadUser is
import AsyncStorage from '@react-native-async-storage/async-storage';
const STORAGE_KEY = 'USER';
export const loadUser = async () => {
try {
const data = await AsyncStorage.getItem(STORAGE_KEY);
if (data !== null) {
return data;
}
} catch (error) {
console.log('Error user loading', error);
}
};
export const saveUser = async (data) => {
try {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(data));
} catch (error) {
console.log(error);
}
};
export const clearUser = async () => {
try {
await AsyncStorage.clear();
} catch (error) {
console.log(error);
}
};
Your fetch, and async storage are all asynchronous.
Try this method instead, by converting it to an async function and using await. This allows your dispatch to be called after the async function is completed.
useEffect(() => {
const fetchData = async() => {
let userData;
let loggedIn;
try {
// setTimeout(() => {
await loadUser().then((data) => {
if (data) {
console.log(data);
userData = data;
loggedIn = true;
}
});
// }, 2000);
dispatch({
type: 'FETCH_CREDS',
user: userData,
loading: false,
isLogin: loggedIn,
});
} catch (e) {
console.log(e);
}
};
fetchData();
},[])
Using Async and Await for asynchronous operations
Alternatively, put your dispatch statement inside the then statement.