Home > front end >  React native navigation v6 authentication flow
React native navigation v6 authentication flow

Time:01-05

I have an app in which I login/register and can signout/logout, I'm using react navigation v6 for the same, and I'm able to login but when I logout it doesn't log me out immediately as it should, I will have to hot restart the app, & then I'm already logged out, it just doesn't log me out without hot restarting, also, while logging out, I get an error/warning:

error:

The action 'NAVIGATE' with payload {"name":"Login"} was not handled by any navigator.
Do you have a screen named 'Login'?
If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
This is a development-only warning and won't be shown in production.

Below is the code I'm using:

Context file:

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);
}

Navigation Screen:

const Navigation = () => {
  const {user, dispatch, loading} = useAuth();
  let navigationRef;

  useEffect(() => {
    setTimeout(() => {
      navigationRef = createNavigationContainerRef(); // To avoid getting error: navigator is undefined while the component is mounting...
    }, 500);
    checkUserLoggedInStatus(dispatch, navigationRef);
  }, [dispatch]);

  return loading ? (
    <LoadingScreen />
  ) : (
    <Stack.Navigator screenOptions={{headerShown: false}}>
      {!user ? (
        <Stack.Group>
          <Stack.Screen name="Login" component={LoginScreen} />
          <Stack.Screen name="Register" component={RegisterScreen} />
        </Stack.Group>
      ) : (
        <Stack.Group>
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Settings" component={SettingsScreen} />
        </Stack.Group>
      )}
    </Stack.Navigator>
  );
};

checkUserLoggedInStatus, login & logout utility function:

export const login = (otp, userId, dispatch) => {
  dispatch(SET_LOADING(true));
  fetch(`${API_URL}/auth/login/`, {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({...}),
  })
    .then(response => response.json())
    .then(data => {
      if (data.error) {
        console.log('[ERROR] While trying to login: ', data.error);
        dispatch(SET_LOADING(false));
      }
      if (data.type === 'success') {
        AsyncStorage.setItem('token', data.data.token)
          .then(response => console.log(response))
          .catch(err => console.log(err));

        const userDetails = data.data.user;
        dispatch(SET_USER_DETAILS(userDetails));
        dispatch(SET_LOADING(false));
      }
    })
    .catch(error => {
      console.log('[ERROR] While logging in: '   error.message);
      dispatch(SET_LOADING(false));
    });
};

export const checkUserLoggedInStatus = (dispatch, navigator) => {
  dispatch(SET_LOADING(true));
  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));
              dispatch(SET_LOADING(false));
            }
            if (data.error) {
              console.log('[INFO] Your token expired.');
              if (navigator.isReady()) {
                navigator.navigate('Login');
              }
            }
          })
          .catch(error => {
            console.log('[ERROR] While fetching profile: '   error.message);
            dispatch(SET_LOADING(false));
          });
      } else {
        dispatch(SET_LOADING(false));
      }
    })
    .catch(error => {
      console.log('[ERROR] While fetching token: '   error.message);
      dispatch(SET_LOADING(false));
    });
};

export const logout = async (dispatch, navigator) => {
  dispatch(SET_LOADING(true));
  await AsyncStorage.removeItem('token');
  navigator.navigate('Login');
  dispatch(SET_LOADING(false));
};

// HERE I LOGOUT FROM MY SETTINGS SCREEN (WHEN I'M AUTHENTICATED).

As you can see the error (first few lines of code), I cannot correctly navigate/logout from my app. Any help would be really appreciated!

CodePudding user response:

Theres two things to adjust in your logout action by the looks of things.

  1. you are removing the token from AsyncStorage, but you still have a user object set in your useAuth hook which is what conditionally renders the stack group containinig login/register, or the stack group with home/settings:
{!user ? (
        <Stack.Group>
          <Stack.Screen name="Login" component={LoginScreen} />
          <Stack.Screen name="Register" component={RegisterScreen} />
        </Stack.Group>
      ) : (
        <Stack.Group>
          <Stack.Screen name="Home" component={HomeScreen} />
          <Stack.Screen name="Settings" component={SettingsScreen} />
        </Stack.Group>
      )}

so the user is not falsy and will still render the home/settings hence you aren't navigating back to the login. Dispatch an action to set that back to null and you'll automatically navigate back to login.

  1. The typical logout flow for use when you are using react-navigation means that when you perform the logout action you don't actually need to do the navigator.navigate('Login') because the default route for that stack will be back to the login (just set this in the navigator), as the screen isn't currently rendered due to the first point I mentioned above (conditionally rendering them, the screen literally doesn't exist at the point you're navigating) so you can safely remove this line.
  •  Tags:  
  • Related