Status of React Navigation drawer? (open or closed

2020-03-01 20:36发布

问题:

I'm building a drawer with React Navigation and want to perform some logic if the user closes the drawer. I don't see anything obvious in the documentation that will allow me to do this. Is anyone aware of a way to do this?

回答1:

You need to custom navigation actions to capture the DrawerClose event:

const MyDrawerNavigator = DrawerNavigator({
    //...
});

const defaultGetStateForAction = MyDrawerNavigator.router.getStateForAction;

MyDrawerNavigator.router.getStateForAction = (action, state) => {

    //use 'DrawerOpen' to capture drawer open event
    if (state && action.type === 'Navigation/NAVIGATE' && action.routeName === 'DrawerClose') {
        console.log('DrawerClose');
        //write the code you want to deal with 'DrawerClose' event
    }
    return defaultGetStateForAction(action, state);
};


回答2:

According to @ufxmeng

import {
  StatusBar,
} from "react-native";

const MyDrawerNavigator = DrawerNavigator({
    //...
});

const defaultGetStateForAction = MyDrawerNavigator.router.getStateForAction;

MyDrawerNavigator.router.getStateForAction = (action, state) => {
    if(state && action.type === 'Navigation/NAVIGATE' && action.routeName === 'DrawerClose') {
        StatusBar.setHidden(false);
    }

    if(state && action.type === 'Navigation/NAVIGATE' && action.routeName === 'DrawerOpen') {
        StatusBar.setHidden(true);
    }


    return defaultGetStateForAction(action, state);
};

See here https://github.com/react-community/react-navigation/blob/673b9d2877d7e84fbfbe2928305ead7e51b04835/docs/api/routers/Routers.md and here https://github.com/aksonov/react-native-router-flux/issues/699



回答3:

This is for anyone using the v2.0+ version of react-navigation. There are now drawer actions you can track.

{
  OPEN_DRAWER: 'Navigation/OPEN_DRAWER',
  CLOSE_DRAWER: 'Navigation/CLOSE_DRAWER',
  TOGGLE_DRAWER: 'Navigation/TOGGLE_DRAWER',
  DRAWER_OPENED: 'Navigation/DRAWER_OPENED',
  DRAWER_CLOSED: 'Navigation/DRAWER_CLOSED'
}

react-navigation.js line 825

However, it seems that implied drawer navigations from say swiping don't fire the OPEN_/CLOSE_ actions, since you didn't manually toggle it. The _OPENED/_CLOSED actions do fire afterwards, though.

const MyDrawerNavigator = createDrawerNavigator(...);
const defaultGetStateForAction = MyDrawerNavigator.router.getStateForAction;

MyDrawerNavigator.router.getStateForAction = (action, state) => {
  switch (action.type) {
    case 'Navigation/OPEN_DRAWER':
    case 'Navigation/DRAWER_OPENED':
      StatusBar.setHidden(true, 'slide');
      break;
      
    case 'Navigation/CLOSE_DRAWER':
    case 'Navigation/DRAWER_CLOSED':
      StatusBar.setHidden(false, 'slide');
      break;
    }

  return defaultGetStateForAction(action, state);
};



回答4:

Working with "react-navigation": "^3.5.1"

const defaultGetStateForAction = DrawerNav.router.getStateForAction;

DrawerNav.router.getStateForAction = (action, state) => {

    if(action){
        if(action.type == 'Navigation/MARK_DRAWER_SETTLING' && action.willShow){   
            StatusBar.setHidden(true);
        } else if(action.type == 'Navigation/MARK_DRAWER_SETTLING' && !action.willShow) {
            StatusBar.setHidden(false);
        }
    }

    return defaultGetStateForAction(action, state);
};



回答5:

I found something similar to asdfghjklm.

Here's my code which is working perfectly, even when you use a swipe to open - and then use a button to close it:

openCloseDrawer = (props) => {
  if (props.navigation.state.index == 1) {
    props.navigation.navigate('DrawerClose');
  } else {
    props.navigation.navigate('DrawerOpen');
  }
}

I execute this function when the user taps on the "Open/Close Drawer" button.



回答6:

Based on Brad Bumbalough, fredrivett (thank you mans) solution I find a more fast response solution (the other solution delays secons in some uses).

const defaultGetStateForAction = StackLogadoDrawer.router.getStateForAction;

StackLogadoDrawer.router.getStateForAction = (action, state) => {
  switch (action.type) {
    case 'Navigation/MARK_DRAWER_SETTLING':
      if (action.willShow == false) {
        console.log('CERRADO');
      } else if (action.willShow == true) {
        console.log('ABIERTO');
      }
      break;
    }

  return defaultGetStateForAction(action, state);
};

This is fired just immediately actions occurs (or very near).

It works on gestures or calling openDrawer()

Anyway I think this is a "must have" easy and direct way in the API



回答7:

Hmm not really seeing much. One way would be to control the opening/closing of the drawer manually using:

this.props.navigation.navigate('DrawerOpen'); // open drawer
this.props.navigation.navigate('DrawerClose'); // close drawer

That way you can wrap your close/open events in a function where you can do whatever you want before opening/closing.

The only other possible solution I saw in their docs was using a custom contentComponent. You can pass a function to the onItemPress(route) event, so maybe you can try hooking into that.



回答8:

This is working fine in React-Native, not sure about React.

For my case I had to change the StatusBar so I did not need know wether the Drawer is closed fully or not, So below code worked for me...

props.navigation.state.drawerMovementDirection === 'closing'
        ? //it is closing not fully closed
        : (props.navigation.state.drawerMovementDirection === 'opening' ||
            props.navigation.state.isDrawerOpen) && (
          //fully opened or opening 
        )

If you don't need to know immediate then you can use below code, and this will work fine and will give you accurate answer whether the Drawer is Open or Not!

Note: This will be delayed answer, in my case it was taking 1 sec.

        props.navigation.state.isDrawerOpen?
                //open
                :
                //close;

If the solution does't work I am very sorry, but above all answer did not worked! So this is the version which is working for me:))



回答9:

For anyone looking to wire it up such that the drawer events are available in one of your Screens or Components instead of the top level app, I was able to wire that up by using screenProps as described in this post. You first set up the screenProps on your app and pass in the router and whatever else you need. You can pull screenProps off the props and use it your screen or component (I wired it up in the constructor in this example), use getStateForAction to setState in your component driven off the router events.

Here is an example (some code removed for clarity)

App.js

import React from 'react';
import { AppLoading } from 'expo';

import {
  createDrawerNavigator,
  createAppContainer,
  createStackNavigator,
} from 'react-navigation';

import { HomeScreen } from './src/screens/HomeScreen.android';
import { LanguageSelectScreen } from './src/screens/LanguageSelectScreen';

export default class App extends React.Component {
  state = {
    isLoadingComplete: false,
    isTilted: false,
  };

  constructor() {
    super();
  }

  render() {
    if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
      return (
        <AppLoading  />
      );
    } else {
      return (
        <MyApp screenProps={MyAppNavigator.router} />
      );
    }
  }
}

const MyAppNavigator = createDrawerNavigator(
  {
    Home: {
      screen: HomeScreen,
    },
    PlayerNameScreen: {
      screen: PlayerNameScreen,
    },
  },
  {
    unmountInactiveRoutes: true,
    initialRouteName: 'PlayerNameScreen',
  },
);

const RootStack = createStackNavigator(
  {
    Main: {
      screen: MyAppNavigator,
    },
    MyModal: {
      screen: LanguageSelectScreen,
    },
  },
  {
    mode: 'modal',
    headerMode: 'none',
  },
);

export const MyApp = createAppContainer(RootStack);

HomeScreen.android.js

import React from 'react';
import {Icon} from 'react-native-elements';

export class HomeScreen extends React.Component {
  static navigationOptions = {
    drawerLabel: () => 'Home',
    drawerIcon: ({ tintColor }) => (
      <Icon
        name="checkerboard"
        type="material-community"
        size={25}
        color={tintColor}
      />
    ),
  };

  constructor(props) {
    super(props);

    const router = props.screenProps;

    const defaultGetStateForAction = router.getStateForAction;

    router.getStateForAction = (action, state) => {
      switch (action.type) {
        case 'Navigation/MARK_DRAWER_SETTLING':
          if (action.willShow == false) {
            console.log('CLOSED');
            this.setState({ isTilted: false });
          } else if (action.willShow == true) {
            this.setState({ isTilted: true });
            console.log('OPEN');
          }
          break;
      }

      return defaultGetStateForAction(action, state);
    };

    this.state = {
      isTilted: false,
    };
  }

  render() {
    const { isTilted } = this.state;

    // ... render using isTilted
  }

}


回答10:

Without Redux integration can be used onNavigationStateChange on router component. Just intercept drawer actions: DrawerOpen and DrawerClose.

Example:

  handleNavigationState = (previous, next, action) => {    
    if (action.routeName === 'DrawerOpen') {
      this.props.setDrawerState(true);
    } else if (action.routeName === 'DrawerClose') {
      this.props.setDrawerState(false);
    }
  }

  render() {
    return (
      <Router onNavigationStateChange={this.handleNavigationState} />
    );
  }


回答11:

I know this is late, but for anyone who is looking for an answer:

The logic for the drawer being open/closed is in:

this.props.navigation.state.routes[0].index

It's 0 for closed, 1 for open.

You can also toggle the Drawers with

this.props.navigation.navigate('DrawerToggle')

instead of

this.props.navigation.navigate('DrawerOpen'); or

this.props.navigation.navigate('DrawerClose');

It seems to be more convenient for me and I have not happened upon any problems yet. Although it's nice to know whether they are toggled or not in order to invoke other actions.

I truly believe React-Navigation has one of the worst documentation that I have ever seen. There are commands that nobody knows about. I could not find the DrawerToggle action in the documents, and I only happened upon it through using console.log(this.props.navigation);