daniel doe
daniel doe

Reputation: 123

How to detect when the drawer is opened in react-navigation?

I'm using react-navigation's DrawerNavigator in my app. I would like to detect when a user drags open the side menu so that i can perform a certain action, e.g dismiss an opened Keyboard. How can i do this? i can't seem to find a solution in the docs. Thank you.

Here is my code

import React from 'react';
import { Dimensions } from 'react-native';
import { Icon } from 'react-native-elements';
import { DrawerNavigator, StackNavigator, addNavigationHelpers } from 'react-navigation';


//redux related imports
import { createStore, combineReducers } from 'redux';
import { Provider, connect } from 'react-redux';

import Attendance from './containers/pages/Attendance';
import Fees from './containers/pages/Fees';
import Exams from './containers/pages/Exams';
import InitializeUser from './containers/pages/InitializeUser';
import Landing from './Landing';
import Login from './containers/pages/Login';
import Search from './containers/pages/Search';
import Staff from './containers/pages/Staff';
import Stats from './containers/pages/Stats';
import Students from './containers/pages/Students';
import Verification from './containers/pages/verify';
import ProfileDetail from './components/pages/ProfileDetail';
import FeesDetail from './containers/pages/FeesDetails';


import MainReport from './containers/pages/Reports/Main_Report';
import AcademicDetails from './containers/pages/Reports/Student_Academic_Details';
import { Constants } from './config';
import ResultsLanding from './containers/pages/Reports/ResultsLanding';
import { CustomDrawerContentComponent } from '../src/components/base/SideMenu';


const screenWidth = Dimensions.get('window').width;
const MainPages = DrawerNavigator({
  StudentsPage: {
    path: '/Students',
    screen: Students
  },
  SearchPage: {
    path: '/Seacrh',
    screen: Search
  },

  Staff: {
    path: '/Staff',
    screen: Staff
  },

  Fees: {
    path: '/Fees',
    screen: Fees
  },
  Stats: {
    path: '/Stats',
    screen: Stats
  },
  Results: {
    screen: ResultsLanding,
    navigationOptions: {
      tintColor: 'red',
      flex: 1,
      drawerIcon: ({ tintColor }) => (
        <Icon
          name="content-paste"
          color={tintColor}
        />
      )
    }
  },
  Attendance:
  {
    path: '/Attendance',
    screen: Attendance
  },
},
  {
    initialRouteName: 'StudentsPage',
    contentOptions: {
      activeTintColor: Constants.ui.THEME,
      activeBackgroundColor: 'rgba(0,0,0,0.1)',
      inactiveTintColor: 'rgba(0,0,0,0.6)',
      labelStyle: {
        fontSize: 12,
        marginLeft: 10,
      },
    },

    drawerWidth: screenWidth > 320 ? 300 : 250,
    contentComponent: CustomDrawerContentComponent

  });

export const Navigator = StackNavigator({
  LandingPage: {
    screen: Landing,
    navigationOptions: {
      header: null
    }
  },
  LoginPage: {
    screen: Login,
    navigationOptions: {
      header: null
    },
  },
  ProfileDetailPage: {
    screen: ProfileDetail,
    headerMode: 'screen',
    navigationOptions: {
      header: null
    }
  },
  FeesDetailPage:
  {
    screen: FeesDetail,
    navigationOptions: {
      header: null
    },
  },
  VerifyPage: {
    screen: Verification,
    navigationOptions: {
      header: null
    },
  },
  InitUserPage: {
    screen: InitializeUser,
    navigationOptions: {
      header: null
    },
  },
  MainPages: {
    screen: MainPages,
    navigationOptions: {
      header: null
    }
  },

  MainReportPage: {
    screen: MainReport,
    navigationOptions: {
      header: null
    }
  },
  ExamsMainPage: {
    screen: Exams,
    navigationOptions: {
      header: null
    }
  },
  AcademicDetailsPage: {
    screen: AcademicDetails,
    navigationOptions: {
      header: null
    }
  },

});  

const initialState = MainPages.router.getStateForAction(
  MainPages.router.getActionForPathAndParams('StudentsPage'));  

const navReducer = (state = initialState, action) => {
  const nextState = MainPages.router.getStateForAction(action, state);
  return nextState || state;
}; 

const appReducer = combineReducers({
  nav: navReducer
});

class App extends React.Component {
  componentWillReceiveProps(nextProps) {
    console.warn('nextProps: ', JSON.stringify(nextProps, null, 4));
  }
  render() {
    return (
      <MainPages 
        navigation={addNavigationHelpers({
        dispatch: this.props.dispatch,
        state: this.props.nav,
      })} />
    );
  }
}

const mapStateToProps = (state) => ({
  nav: state.nav
});

const AppWithNavigationState = connect(mapStateToProps)(App);

const store = createStore(appReducer);

class Root extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <AppWithNavigationState />
      </Provider>
    );
  }
}

Here is a screenshot of the error i get when i run the code

enter image description here

Upvotes: 10

Views: 12694

Answers (5)

Sadia Chaudhary
Sadia Chaudhary

Reputation: 177

In navigation 6x, you can do the following to get the drawer status.

import { useDrawerStatus } from '@react-navigation/drawer';
const isDrawerOpen = useDrawerStatus() === 'open';

here is the full documentation. https://reactnavigation.org/docs/drawer-navigator/#events

Upvotes: 0

wentris
wentris

Reputation: 503

Simply, if you use react-navigation v5 -

import { useIsDrawerOpen } from '@react-navigation/drawer'

const isOpen: boolean = useIsDrawerOpen()

then you have to subscribe on "isOpen" flag by hook useEffect:

useEffect(() => {
    if (!isOpen) {
      // Your dismiss logic here 
    }
}, [isOpen])

Your custom Drawer can look like DrawerContent

Hope it helped

Upvotes: 10

Trần Hạnh
Trần Hạnh

Reputation: 147

  • this.props.navigation.state.isDrawerOpen => true/false

Upvotes: 1

Francois Nadeau
Francois Nadeau

Reputation: 7463

This is an old thread, but I wanted to share how I managed to do this.

Add the following code inside your app (preferably where you create the DraweStack.)

const defaultGetStateForAction = DrawerStack.router.getStateForAction;
DrawerStack.router.getStateForAction = (action, state) => {
  switch (action.type) {
    case "Navigation/DRAWER_OPENED":
    case "Navigation/MARK_DRAWER_ACTIVE":
      Keyboard.dismiss();
      break;
  }

  return defaultGetStateForAction(action, state);
};

There are various action.types. For instance:

  • Navigation/MARK_DRAWER_ACTIVE is called when the drawer starts to open
  • Navigation/MARK_DRAWER_SETTLING is called when the drawer has opened.
  • etc.

Cheers.

Upvotes: 1

Pat Needham
Pat Needham

Reputation: 5928

I was able to detect the DrawerNavigator's open and close side menu actions by following the Redux Integration guide and modifying it to use a DrawerNavigator instead of StackNavigator. Here is what I have inside my index.ios.js file. Near the bottom within the App class I use componentWillReceiveProps which displays a warning every time the drawer opens or closes.

import React from 'react';
import {
  AppRegistry,
  Image,
  Text,
  View,
  ScrollView
} from 'react-native';
import {DrawerNavigator, DrawerItems, addNavigationHelpers } from 'react-navigation';
import { Provider, connect } from 'react-redux'
import { createStore, combineReducers } from 'redux'

class MyHomeScreen extends React.Component {
  static navigationOptions = {
    drawerLabel: 'Home',
    drawerIcon: ({ tintColor }) => (
      <Image
        source={require('./images/Groups.png')}
        style={{tintColor: tintColor, width: 26, height: 26}}/>
    ),
  };
  render() {
    return (
      <View>
        <Text>Home Screen</Text>
      </View>
    );
  }
}

class MyNotificationsScreen extends React.Component {
  static navigationOptions = {
    drawerLabel: 'Notifications',
    drawerIcon: ({ tintColor }) => (
      <Image
        source={require('./images/Events.png')}
        style={{tintColor: tintColor, width: 26, height: 26}}/>
    ),
  };
  render() {
    return (
      <View>
        <Text>Notifications Screen</Text>
      </View>
    );
  }
}

const NavDemo = DrawerNavigator({
  Home: { screen: MyHomeScreen },
  Notifications: { screen: MyNotificationsScreen }
}, {
  tabBarOptions: {
    activeTintColor: '#e91e63',
  },
  drawerWidth: 200,
  drawerPosition: 'left',
  contentComponent: props => <ScrollView><DrawerItems {...props} /></ScrollView>
});

const initialState = NavDemo.router.getStateForAction(NavDemo.router.getActionForPathAndParams('Home'));

const navReducer = (state = initialState, action) => {
  const nextState = NavDemo.router.getStateForAction(action, state);
  return nextState || state;
};

const appReducer = combineReducers({
  nav: navReducer
});

class App extends React.Component {
  componentWillReceiveProps(nextProps) {
    console.warn('nextProps: ' + JSON.stringify(nextProps, null, 4))
  }
  render() {
    return (
      <NavDemo navigation={addNavigationHelpers({
        dispatch: this.props.dispatch,
        state: this.props.nav,
      })} />
    );
  }
}

const mapStateToProps = (state) => ({
  nav: state.nav
});

const AppWithNavigationState = connect(mapStateToProps)(App);

const store = createStore(appReducer);

class Root extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <AppWithNavigationState />
      </Provider>
    );
  }
}

AppRegistry.registerComponent('NavDemo', () => Root);

When I open the drawer and expand the warning nextProps looks like this:

enter image description here

And then after I close the drawer, the new warning appears like this:

enter image description here

nextProps.nav is an object with two keys, routes and index. When the drawer opens, index becomes 1, and when it closes, index becomes 0.

Using that information, you can add an if statement to perform your necessary actions when the drawer opens.

Upvotes: 2

Related Questions