I have an Auth screen and a HomeScreen, and to manage the state I am using Context API in which I have 2 states -> 1) user
state on which I'm deciding whether I am authenticated or not (while fetching info from the server), 2) loading
state to show a loading screen when all this is happening. The problem is, everything is working, even setting the state of user
from null to a well defined object coming from my API, and in the same way I'm changing my loading
state but it is not changing, it is false
everytime despite I'm dispatching using the same method.
Context file:
import React, { createContext, useContext, useReducer } from 'react';
import {UserReducer} from './reducers';
const initialState = {
user: null,
loading: false,
};
const UserContext = createContext(initialState);
export const UserProvider = ({children}) => {
const [globalState, dispatch] = useReducer(UserReducer, initialState);
return (
<UserContext.Provider value={{
user: globalState.user,
loading: globalState.loading,
dispatch,
}} >
{children}
</UserContext.Provider>
);
};
export default function() {
return useContext(UserContext);
}
Actions:
export const SET_USER_DETAILS = (userDetails) => {
return {
type: 'SET_USER_DETAILS',
payload: userDetails,
};
};
export const SET_LOADING = (loadingState) => {
return {
type: 'SET_LOADING',
payload: loadingState,
};
};
Reducer:
export const UserReducer = (state, action) => {
switch (action.type) {
case 'SET_USER_DETAILS':
return {
...state,
user: action.payload,
};
case 'SET_LOADING':
return {
...state,
loading: action.payload,
};
default:
return state;
}
};
Main Navigation File:
const Navigation = () => {
const {user, dispatch, loading} = useAuth();
console.log(user); // after successful fetch from api, I'm getting the desired user data.
console.log(loading); // PROBLEM => always false.
useEffect(() => {
checkUserLoggedInStatus(dispatch);
}, [dispatch]);
return loading ? (
<LoadingScreen />
) : !user ? (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="Verify" component={OTPVerificationScreen} />
</Stack.Navigator>
) : (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Message" component={MessageScreen} />
</Stack.Navigator>
);
};
The checkUserLoggedInStatus
function:
export const checkUserLoggedInStatus = (dispatch) => {
dispatch(SET_LOADING(true)); // NOT WORKING/NOT CHANGING THE LOADING STATE
AsyncStorage.getItem('token')
.then((token) => {
if (token) {
fetch(`${API_URL}/user/`, {
method: 'GET',
headers: {
...
},
})
.then(response => response.json())
.then((data) => {
if (data.type === 'success') {
const details = data.data.user;
dispatch(SET_USER_DETAILS(details)); // THIS IS WORKING FINE.
}
})
.catch((error) => {
console.log(error.message);
});
}
})
.catch((error) => {
console.log(error.message);
});
dispatch(SET_LOADING(false)); // NOT WORKING/NOT CHANGING THE LOADING STATE
};
I cannot understand what I am doing wrong because I am doing the same thing with SET_USER_DETAILS
action/reducer function but I don't know what's wrong with the loading
state.
SUMMARY: Not able to render LoadingScreen
based on loading
state from UserContext
.
If anyone could help me, I would really appreciate it!
Thank you!
CodePudding user response:
The problem with your implementation is that in the checkUserLoggedInStatus
you are setting the loading state false outside the then
and catch
blocks. Since you are performing asynchronous task and need to set the loading to false after this async task is completed, you need to set the loading to false in either then
or catch
.