Leminur
Leminur

Reputation: 361

How to dynamically show Drawer items in React-Navigation V5?

I am having a bit of trouble trying to dynamically show different items at React-Navigation drawer.

I have a drawer that needs to show different items if the user is logged in or not. In this case, if the user is logged in, the buttons for "RegisterScreen" and "LoginScreen" should NOT be shown and the "Log Out" button should be shown. However, if the user is NOT logged in, then both Register and Login screens should be shown and the Log out button should not.

I have successfully shown the buttons using the code below (Except for the Logout button, I did not do it yet). However, once the user logs out, the app redirects them to the login screen. Since I did not declare them in the drawer, I get the following error:

console.error: The action 'NAVIGATE' with payload {"name":"LoginModal"} was not handled by any navigator.

I do understand that the screens were not added, but I have no idea how they can be hidden when the user is logged in.

Code:

//Added DrawerItemList for the Logout button.
  const CustomDrawerContent = (props) => {
    return (
      <DrawerContentScrollView {...props}>

        <DrawerItemList {...props} />
        <DrawerItem
          label="Logout"
          onPress={async () => {
            props.navigation.closeDrawer();
            await LogOut().then((result) => {
              props.navigation.navigate('LoginModal');
            });
          }}
        />

      </DrawerContentScrollView>
    );
  };

  const [drawerCategories, setDrawerCategories] = useState([]);

//Checks if the user is logged in and sets Login and Register screens in the state.
  useEffect(() => {
    let mounted = true;
    if (mounted) {
      AsyncStorage.getItem('userId').then((result) => {
        console.log(result);
        if (result == null) {
          setDrawerCategories([
            ...drawerCategories,
            {name: 'LoginModal', component: {LoginScreen}},
            {name: 'RegisterModal', component: {RegisterScreen}},
          ]);
        }
      });
    }
    return () => (mounted = false);
  }, []);

  return (
    <NavigationContainer>
      <RootDrawer.Navigator
        drawerType="front"
        initialRouteName="Home"
        mode="modal"
        drawerContent={(props) => <CustomDrawerContent {...props} />}>
        <RootDrawer.Screen name="Home" component={MainStackScreen} />

//Add the Register and Login buttons here
        {drawerCategories.map((drawer) => {
          <RootDrawer.Screen name={drawer.name} component={drawer.component} />;
        })}

      </RootDrawer.Navigator>
    </NavigationContainer>
  );

Can you please help me figuring out how to fix this error? Thank you so much!

Upvotes: 1

Views: 2309

Answers (1)

satya164
satya164

Reputation: 10143

Not sure what your LogOut function does, but unless it actually re-runs the effect so that your LoginModal screen is defined, there won't be a screen named LoginModal to navigate to.

Looking at your code, your effect won't re-run after you logout. Instead of setting the list of screens in the effect, you should arrange your code to something like this:

  1. Global store like Redux, React Context or whatever you prefer to store the userId
  2. The useEffect hook should restore try to restore the userId from AsyncStorage like you're doing now, but after restoring, it should update your global store to update the userId there instead of setting the screens
  3. Your logOut function should delete the userId from AsyncStorage as well as update the global store so that it's null.
  4. Inside your component, you should conditionally define the screens based on if userId is null, and they'll automatically change on login or logout

The docs for authentication flow show an example of how this works using a token and stack navigator: https://reactnavigation.org/docs/auth-flow/

You can use similar strategy with an user id and a drawer navigator.

Upvotes: 2

Related Questions