EmpireJones
EmpireJones

Reputation: 3086

Open one of two drawers from button in react navigation 5

It was previously possible to open a specific drawer from a button, using getCustomActionCreators, prior to v5 of React Navigation.

An example of doing that is here: React Native two drawers on one screen

However, it looks like this function has been removed. Is there a way to do this in React Navigation 5?

Upvotes: 0

Views: 1053

Answers (1)

EmpireJones
EmpireJones

Reputation: 3086

This is possible by creating a custom router.

Here are a few key pieces of insight:

  • navigators bubble up actions which they don't handle. This means that we need to create a custom action for each of our drawers.
  • In order to create custom actions, we need to create custom routers.
  • In order to use the custom routers, we need to build a new navigator.

These notes have been verified against the following versions:

  • @react-navigation/native: 5.8.0
  • @react-navigation/drawer: 5.10.0
  • @react-navigation/routers: 5.5.0

Custom action:

export const openSecondDrawerAction = { type: 'OPEN_SECOND_DRAWER' };

Helper function to dispatch custom action (optional)

export const openSecondDrawer = navigation => {
    navigation.dispatch(openSecondDrawerAction);
};

Custom router

import { DrawerRouter } '@react-navigation/routers';

const SecondDrawerRouter = options => {
  const router = DrawerRouter(options);
  return {
    ...router,
    getStateForAction: (state, action, options) => {
      switch (action.type) {
        case 'OPEN_SECOND_DRAWER':
          // CATCH THIS ACTION BUT MODIFY TO AN OPEN_DRAWER
          return router.getStateForAction(state, {
            ...action,
            type: 'OPEN_DRAWER'
          }, options);
        case 'OPEN_DRAWER':
          // DO NOT HANDLE THIS ACTION, LET IT BUBBLE TO THE PRIMARY DRAWER
          return null;
        default:
          return router.getStateForAction(state, action, options);
      }
    }
  };
};

Custom navigator

This is based on the code from createDrawerNavigator.

import { useNavigationBuilder, createNavigatorFactory } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { DrawerView, DrawerActions } from '@react-navigation/drawer';

const createDrawerNavigatorWithRouter = router => {
  function DrawerNavigator({ initialRouteName, openByDefault, backBehavior, children, screenOptions, ...rest }) {
    const { state, descriptors, navigation } = useNavigationBuilder(router, {
      initialRouteName,
      openByDefault,
      backBehavior,
      children,
      screenOptions
    });
    return <DrawerView {...rest} state={state} descriptors={descriptors} navigation={navigation} />;
  }
  return createNavigatorFactory(DrawerNavigator)();
};

Create navigator instance(s) Now we can control two drawers, where the first is handled as a standard drawer, and the second uses the modified router:

const FirstDrawer = createDrawerNavigator()
const SecondDrawer = createDrawerNavigatorWithRouter(SecondDrawerRouter)

Nesting your navigators It is necessary to nest the navigators as follows:

<FirstDrawer.Navigator>
    // then wrap the required FirstDrawer.Screen entry/ies with:
    <SecondDrawer.Navigator>
    </SecondDrawer.Navigator>
</FirstDrawer.Navigator>

This is a conceptual view, as its unfortunately not as simple as this to define nested navigators - you need to define Screen components which are themselves wrapped in the second navigator

Repeat It should be possible to duplicate the above (with different action names) for a third or more drawers. createDrawerNavigatorWithRouter can be reused without duplication, as it's generic to drawer navigators.

Upvotes: 1

Related Questions