Home > OS >  Expo / ReactNative - React Navigation - Conditional Auth Flow
Expo / ReactNative - React Navigation - Conditional Auth Flow

Time:09-17

I trying use conditional auth flow.

Code:

return (
    
    <ApolloProvider client={client}>
      
    <NavigationContainer>
      
      
    <AuthProvider>
   
      <Stack.Navigator>
      
    {userToken ? 
    // User is signed in, show Home Screen
      
    <Stack.Screen name="Home" component={HomeScreen} />

    :
      // No token found, user isn't signed in, show the signin screen
     
      <Stack.Screen
        name="SignIn"
        component={SignInScreen}
        options={{
          headerShown: false,
        }}
      />
      
       
     
      }
      
  </Stack.Navigator>
  
  </AuthProvider>
      </NavigationContainer>
    
      </ApolloProvider>
      
      
  );
}

On both screens (home, signin) I displaying userToken state to easy see if that change.

When I sign in, userToken change from null to token string, but screen stuck on SignInScreen. When I SignOut, userToken change from token string to null, but screen is always the same.

I don't understand why is not working. Read a lot, check examples and everywhere is looks the same for me. Can't find any differences what can make that not working.

Will be able someone to help me with that?

Kind Regards, Oskar

Edit: From where I getting userToken

/// Auth.tsx

import * as React from 'react';
import { getToken, setToken, removeToken } from './utils';

interface AuthState {
  userToken: string | undefined | null;
  status: 'idle' | 'signOut' | 'signIn';
}
type AuthAction = { type: 'SIGN_IN'; token: string } | { type: 'SIGN_OUT' };

type AuthPayload = string;

interface AuthContextActions {
  signIn: (data: AuthPayload) => void;
  signOut: () => void;
}

interface AuthContextType extends AuthState, AuthContextActions {}
const AuthContext = React.createContext<AuthContextType>({
  status: 'idle',
  userToken: null,
  signIn: () => {},
  signOut: () => {},
});

// In case you want to use Auth functions outside React tree
export const AuthRef = React.createRef<AuthContextActions>();

export const useAuth = (): AuthContextType => {
  const context = React.useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be inside an AuthProvider with a value');
  }
  return context;
};

export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  const [state, dispatch] = React.useReducer(AuthReducer, {
    status: 'idle',
    userToken: null,
  });

  React.useEffect(() => {
    const initState = async () => {
      try {
        const userToken = await getToken();
        if (userToken !== null) {
          dispatch({ type: 'SIGN_IN', token: userToken });
        } else {
          dispatch({ type: 'SIGN_OUT' });
        }
      } catch (e) {
        console.log('initState error ' e)
      }
    };

    initState();
  }, []);

  React.useImperativeHandle(AuthRef, () => authActions);

  const authActions: AuthContextActions = React.useMemo(
    () => ({
      signIn: async (token: string) => {
        dispatch({ type: 'SIGN_IN', token });
        await setToken(token);
      },
      signOut: async () => {
        await removeToken(); // TODO: use Vars
        dispatch({ type: 'SIGN_OUT' });
      },
    }),
    []
  );

  return (
    // <AuthContext.Provider value={{ ...state, ...authActions }}>
    <AuthContext.Provider value={{ ...state, ...authActions }}>
      {children}
    </AuthContext.Provider>
  );
};

const AuthReducer = (prevState: AuthState, action: AuthAction): AuthState => {
  switch (action.type) {
    case 'SIGN_IN':
      return {
        ...prevState,
        status: 'signIn',
        userToken: action.token,
      };
    case 'SIGN_OUT':
      return {
        ...prevState,
        status: 'signOut',
        userToken: null,
      };
  }
};

CodePudding user response:

Try to use one NavigationContainer for signed and one NavigationContainer for unsigned

see this example: https://github.com/akinncar/expo-star-wars/blob/master/src/routes.tsx

CodePudding user response:

Also, your useEffect is indirectly dependent on "userToken" which is in your reducer context. Though it gets it a roundabout way (await getToken()), it won't check again any more after initial component load, because of your empty (i.e. []) dependency block, i.e.:

React.useEffect(() => {
    const initState = async ....
    initState();
  }, []); 

Consider changing that last line to be a reference to your reducer's usertoken state that changes so it'll reactively update itself or something that would have a similar effect.

  • Related