React Native - Creating Navigator dynamically with React Navigation - react-native

I am building a mobile application with React Native and I am using React Navigation to build a navigator inside my application. React navigation provided me a good way to handle nested Tab bars inside a drawer which is also inside a Stack Navigator.
The problem is that I need to specify components so that I can provide these into the Tab Bar. Lets say we have to fetch some categories from an API and we do not know how many categories are inside the data. Besides, I could not figure out that even if I try to fetch data at start, the navigator and redux configuration takes place at start which means the application has to know the components in those tab navigators. I could not figure out that even if I fetched the data from the API, how I can create multiple components and while stopping the application configuration.
The code below, just demonstrates how I implemented the tab bar. This code works in index.js because as I mentioned before, the application have to know the components inside the Navigator.
const TabStack = TabNavigator({
Food: { screen: FoodStack},
Drink : { screen: DrinkStack },
HealthCare : { screen: SnackProducts },
Snacks: { screen: SnackStack },
},
{
tabBarComponent : props => <CustomTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Food',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
Thanks
here the root code
import { AppRegistry } from 'react-native';
import React from 'react';
import { Text, Image, ScrollView, View, List, ListItem, TouchableWithoutFeedback } from 'react-native';
import { Icon, Avatar } from 'react-native-elements';
import { Provider, connect } from 'react-redux'
import thunkMiddleware from 'redux-thunk'
import { createStore, applyMiddleware, combineReducers } from 'redux';
import {
addNavigationHelpers, StackNavigator,
DrawerNavigator,
DrawerItems,
TabNavigator,
TabView,
TabBarTop,
NavigationActions
} from 'react-navigation';
// importing starting screen
import StartingContainer from './src/containers/StartingScreen/StartingContainer';
// Menu Containers
import MenuCredentials from './src/containers/MenuCredentials';
// Containers
import LoginContainer from './src/containers/LoginContainer';
import PhoneNumberValidation from './src/containers/SubLoginContainers/PhoneNumberValidation';
import MainOrderContainer from './src/containers/OrderContainers/MainOrderContainer';
import MainCartContainer from './src/containers/CartContainers/MainCartContainer';
// Components
// Login Components
import SMSLogin from './src/containers/SubLoginContainers/SMSLogin';
// Profil Components
import Profil from './src/components/ProfileComponents/Profile';
import AdressComponent from './src/components/ProfileComponents/AdressComponent';
import SettingsComponent from './src/components/ProfileComponents/SettingsComponent';
import creditCardComponent from './src/components/ProfileComponents/creditCardComponent';
// Reducers
import initialReducer from './src/reducers/initialReducer';
import cartReducer from './src/reducers/cartReducer';
import starterReducer from './src/reducers/starterReducer';
// import tab bar containers
import FoodProducts from './src/containers/TabBarContainers/FoodProducts';
import HealthProducts from './src/containers/TabBarContainers/HealthProducts';
import SnackProducts from './src/containers/TabBarContainers/SnackProducts';
// Building Navigation
import MenuItem from './src/containers/MenuItemContainer/MenuItem';
import CustomTabItems from './src/containers/CustomTabItems';
import CustomSubTabItems from './src/containers/CustomSubTabItems';
import DrawerButton from './src/containers/DrawerButton';
// Tab Bar Navigation
const ChocolateStack = TabNavigator({
Tadelle: { screen: MenuItem},
Milka: { screen: MenuItem},
},
{
tabBarComponent : props => <CustomTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Tadelle',
swipeEnabled: true,
tabBarOptions: {
scrollEnabled: true
},
})
const SnackStack = TabNavigator({
Çikolatalar: { screen: MenuItem},
Gofretler: { screen: MenuItem},
Krakerler: { screen: MenuItem},
Bisküviler: { screen: MenuItem},
Kuruyemişler: { screen: MenuItem},
},
{
tabBarComponent : props => <CustomSubTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Çikolatalar',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
const DrinkStack = TabNavigator({
'Gazlı İçecekler': { screen: MenuItem},
'Soğuk Çaylar': { screen: MenuItem},
'Alkol': { screen: MenuItem},
'Süt Ürünleri': { screen: MenuItem},
'Spor İçecekleri': { screen: MenuItem},
},
{
tabBarComponent : props => <CustomSubTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Alkol',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
const FoodStack = TabNavigator({
Sandviç : { screen: MenuItem},
Çorba: { screen: MenuItem},
},
{
tabBarComponent : props => <CustomSubTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Sandviç',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
const TabStack = TabNavigator({
Food: { screen: FoodStack},
Drink : { screen: DrinkStack },
Health : { screen: SnackProducts },
Snacks: { screen: SnackStack },
},
{
tabBarComponent : props => <CustomTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Food',
swipeEnabled: true,
tabBarOptions : {
tabStyle : {
width : 250
},
scrollEnabled : true
}
})
// cart navigation will be drawernavigator and drawerItems will be custom !!
const CartNavigation = StackNavigator({
Cart: {
screen: MainCartContainer,
}
},
{
headerMode: 'float',
navigationOptions: ({ navigation }) => ({
title: 'Sepet',
headerLeft: <Icon
name='arrow-back'
color='#517fa4'
onPress={() => navigation.navigate('drawerStack')}
/>,
headerRight:
<Icon
name='payment'
color='#517fa4'
onPress={() => navigation.navigate('drawerStack')}
/>
})
}
)
const DrawerStack = DrawerNavigator({
Sipariş: { screen: TabStack },
Profil: {
screen: Profil ,
navigationOptions : ({ navigation }) => ({
title : 'Profilim',
})
},
Adreslerim: {
screen: AdressComponent,
navigationOptions: ({ navigation }) => ({
title: 'Teslimat Adreslerim'
})
},
Ayarlar: { screen: SettingsComponent }
},
{
drawerPosition: 'left',
headerMode : 'none',
navigationOptions: ({ navigation }) => ({
headerStyle: { backgroundColor: '#87CEFA' },
headerRight: <Icon
name='shopping-cart'
color='#517fa4'
onPress={() => navigation.navigate('cartStack')}
/>,
}),
contentOptions: {
inactiveTintColor: 'white',
activeTintColor: 'purple',
style: {
marginTop: 80,
marginLeft: 25,
}
},
contentComponent: props => <MenuCredentials {...props} />
})
const DrawerNavigation = StackNavigator({
DrawerStack: {
screen: DrawerStack
}},
{
style : {
leftDrawerWidth : 40
},
index : 0,
navigationOptions : ({ navigation }) => ({
headerStyle: { backgroundColor: '#87CEFA' },
gesturesEnabled : false,
headerRight : <Icon
name='shopping-cart'
color='#517fa4'
onPress={() => navigation.navigate('cartStack')}
/>,
headerLeft: <Icon
name='menu'
color='#517fa4'
onPress={() => {
console.log(navigation.state.routes[0]);
navigation.navigate({
key : null,
index : 0,
action : [
navigation.navigate('DrawerToggle')
]
})
}}
/>
}),
initialRouteParams : {
name : 'Welcome'
}
}
)
const LoginStack = StackNavigator({
Login: {
screen: LoginContainer,
navigationOptions: ({ navigation }) => ({
title: ' GİZLİ UYGULAMA ! '
})
},
Ss: {
screen: SMSLogin,
navigationOptions: ({ navigation }) => ({
title: ' SMS ONAYI '
})
},
PhoneNumberValidation: {
screen: PhoneNumberValidation,
navigationOptions: ({ navigation }) => ({
title: 'Kaydolma'
})
},
},{
headerMode : 'none',
initialRouteName : 'Login'
})
// IMPORTANT NOTE ***!!!
// CARRY drawerStack to the PrimaryNavigator !!
// CHANGE LoginContainer so that it will navigate to the drawerStack
// NOT FROM ACTION BUT FROM COMPONENT INSIDE COMPONENTWILLUPDATE
// BY CHANGING isAuth variable in initialReducer !!
const PrimaryNavigator = StackNavigator({
loginStack: {
screen: LoginStack
},
cartStack: {
screen: CartNavigation
},
drawerStack: {
screen: DrawerNavigation
},
starter : {
screen : StartingContainer
}
},
{
headerMode: 'none',
title: 'Main',
initialRouteName : 'starter'
}
)
const navReducer = (state, action) => {
const nextState = PrimaryNavigator.router.getStateForAction(action, state);
// Simply return the original `state` if `nextState` is null or undefined.
return nextState || state;
};
// combining Reducers
const AppReducer = combineReducers({
initialR: initialReducer,
cartR: cartReducer,
starterR : starterReducer,
nav: navReducer
})
// Creating redux store
const store = createStore(
AppReducer,
applyMiddleware(thunkMiddleware)
)
// Navigation initilizator to App
class App extends React.Component {
render() {
return (
<PrimaryNavigator navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav
})}
/>
)
}
}
const mapStateToProps = (state) => ({
nav: state.nav
})
const AppWithNavigationState = connect(mapStateToProps)(App);
class brilliantApp extends React.Component{
render(){
return(
<Provider store={store}>
< AppWithNavigationState />
</Provider>
)
}
}
AppRegistry.registerComponent('brilliantApp', () => brilliantApp);

Your TabStack file:
const CATEGORIES = {
"Food": { screen: FoodStack },
// ...
}
export default (screenNames) => {
const screens = screenNames.reduce((total, name) => ({...total, [name]: CATEGORIES[name]}), {})
const TabStack = TabNavigator(screens,
{
tabBarComponent : props => <CustomTabItems props={props}/>,
tabBarPosition: 'top',
animationEnabled : true,
initialRouteName : 'Food',
swipeEnabled: true,
tabBarOptions : {
scrollEnabled : true
}
})
return TabStack
}
Your Root file:
import getTabStack from './TabStack'
class Root extends Component {
state = {
categoriesNames: null
}
componentWillMount() {
// assuming result is ["Food", "Drink", ... ]
Api.fetchCategories().then((result) => {
this.setState({ categoriesNames: result })
})
}
render() {
const { categoriesNames } = this.state
if (!categoriesNames) {
return <SplashScreen />
}
const TabStack = getTabStack(categoriesNames)
return (
<Provider store={store} >
<TabStack />
</Provider>
);
}
}

Here I would like to post a method for creating tab bar according to the data we fetched from some API etc programmatically.
Here we fetch the data from API in this example, this code from the top level component :
renderShopTab() {
const { client } = this.props;
try {
const { categories } = client.readQuery({
query: gql`
{
categories{
id
name
products{
id
name
price
quantity
}
}
}`
})
console.log("Categories :" + categories);
return (
<ShopCreator categories={categories} />
)
} catch (error) {
console.log("Error occured creating the categories due to the : " + error);
return (
<View>
<Text>
Loading...
</Text>
</View>
)
}
}
This code snippet is from creator of the tab bar dynamically :
export const ShopCreator = ({ categories }) => {
// This script will create a TabNavigator for categories and StackNavigators for each member of the categories !
let categoryStack = {};
let routes = {};
categories.forEach((category) => {
if (category.products.length > 0) {
const { catname } = category.name;
if (category.name != undefined) {
routes[category.name] = {
screen: StackNavigator({
'isim': {
screen: ProductPage
}
},{
headerMode : 'none',
initialRouteParams : {
categoryName : category.name,
products : category.products
}
})
}
}
} else {
console.log("This category has no products !");
}
})
console.log("OHA : " + JSON.stringify(routes));
const ShopCatTabNav = TabNavigator(routes, {
tabBarPosition: 'top',
tabBarComponent: props => <TabMenuItems props={props} />
})
return <ShopCatTabNav />
}
As last , I will show you customized tab navigation bar I built :
const TabMenuItems = ({props}) => {
const { activeTintColor, tab, tabbar, tabText, inactiveTintColor } = styles;
const { index } = props.navigation.state;
return(
<View>
<ScrollView contentContainerStyle={{ flex : 1 }} horizontal showsHorizontalScrollIndicator={false} style={{backgroundColor : '#FFAEB9'}}>
{
props.navigation.state.routes.length ? (
props.navigation.state.routes.map((route,number)=>{
const focused = ( index === number ) ? '#1874CD' : '#FF6A6A';
const tintColor = focused ? activeTintColor : inactiveTintColor;
return (
<TouchableWithoutFeedback
key={route.key}
onPress={() => {
props.jumpToIndex(number)
}}
delayPressIn={0}
>
<View style={{marginLeft : 20, marginTop : height / 40, shadowOpacity : 25, alignSelf : 'flex-start' }}>
<Text style={{borderRadius : 5, fontWeight : 'bold', borderWidth :2, paddingTop : 5,color : 'white', height : height/18, width : width/5,textAlign : 'center', backgroundColor : focused, borderStyle: 'dashed',borderColor : '#CD2626'}}>
{props.getLabel({route, number})}
</Text>
</View>
</TouchableWithoutFeedback>
)
})
) : null
}
</ScrollView>
</View>
)
}
export default TabMenuItems;

It's been a while this q was posted but if there are still some people looking at this. I'd rather using react-native-navigation instead this library.

Related

Set title in createStackNavigator onPress

I am making react native app. I have button and onPress i want to send headerTitle to createStackNavigator but i have some errors. I am sending title with setParams and in createStackNavigator i get it with getParam.
Why it is not working how is correct?
Code:
// page.js
<TouchableOpacity
onPress={() => navigate('Page2', {
this.props.navigation.setParams({Title: 'Title'});
})}>
// createStackNavigator
export default createStackNavigator(
{
Main: {
screen: Page1,
},
Page2: {
screen: Page2,
navigationOptions: ({ navigation }) => ({
headerTitle: navigation.state.getParam('Title', 'Default Title'),
}),
},
},
{
initialRouteName: 'Main',
}
);
The problem appears to be occurring in the operating order of the app.
Because the value is already obtained before the value is set, the value must be null or undefind, which is the value before the setting.
Try using it as a global function.
globalScreen
let TITLE = "defaultTitle"
function setT(data){
TITLE = data;
}
function getT() {
return TITLE;
}
export { setT, getT }
Usage
// page.js
import {setT } from "globalScreen"
<TouchableOpacity
onPress={() => {
setT("page2");
navigate('Page2'); }}>
// createStackNavigator
import {getT } from "globalScreen"
export default createStackNavigator(
{
Main: {
screen: Page1,
},
Page2: {
screen: Page2,
navigationOptions: ({ navigation }) => ({
headerTitle: getT(),
}),
},
},
{
initialRouteName: 'Main',
}
);

Component with fewer than 1 type argumanet in class delcaration

/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import { createBottomTabNavigator, createSwitchNavigator, createStackNavigator, createAppContainer } from 'react-navigation';
import Event from './src/components/event/Event.js';
import Chat from './src/components/chat/Chat.js';
import Signup from "./src/components/signup/Signup.js";
import Verif1 from "./src/components/signup/Verif1.js";
import NewEvent from "./src/components/event/Newevent.js";
import EditUser from "./src/components/user/Edituser";
import NewUser from "./src/components/user/Newuser";
import io from 'socket.io-client';
import GLOBAL from "./src/lib/global";
import SplashScreen from "./src/components/splashscreen/SplashScreen";
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
//socket.io
const socket = io(GLOBAL.BASE_URL, {
//const socket = io(GLOBAL.BASE_URL, {
transports: ['websocket'],
jsonp: false
});
console.log("socket id in App.js : ", socket.id);
socket.on('disconnect', (reason) => {
// ...
if (reason === 'io server disconnect') {
// the disconnection was initiated by the server, you need to reconnect manually
socket.connect();
}
// else the socket will automatically try to reconnect
});
const ChatWithSocket = (props) => (<Chat {...props} socket={socket} />)
const EventStack = {
Event: {
screen: Event,
navigationOptions: {
title: 'Event',
},
},
NewEvent: {
screen: NewEvent,
navigationOptions: {
title: 'New Event',
},
},
Chat: {
screen: ChatWithSocket,
navigationOptions: {
title: 'Chat',
},
},
};
const SignupStack = {
Signup: {
screen: Signup,
navigationOptions: {
title: 'Signup',
},
},
Verif1: {
screen: Verif1,
navigationOptions: {
title: 'Verify User',
},
},
};
const UserStack = {
NewUser: {
screen: NewUser,
navigationOptions: {
title: 'New User',
},
},
/* EditUser: {
screen: EditUser,
navigationOptions: {
title: 'Edit User',
},
}, */
};
const bottomTabNavOptions = {
defaultNavigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, tintColor }) => {
const { routeName } = navigation.state;
console.log("Navigation : ", navigation.dangerouslyGetParent().getParam('params'));
let iconName;
if (routeName === 'Event') {
iconName = `ios-information-circle${focused ? '' : '-outline'}`;
} else if (routeName === 'User') {
iconName = `ios-options${focused ? '' : '-outline'}`;
}
return <Text>Hello the world!</Text>
},
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
},
};
class App extends React.Component { //<<<=== this line cause error
EventRoute = (initRoute) => {
createStackNavigator(EventStack, {
initialRouteName: initRoute,
})
};
UserRoute = (initRoute) => {
createStackNavigator(UserStack, {
initialRouteName: initRoute,
})
};
bottomTabScreen = () => {
//if there is a token and user
if (this.props.token && this.props.user) {
return createBottomTabNavigator(
{
Event: {screen: this.EventRoute("Event")},
User: {screen: this.UserRoute("User")},
}, bottomTabNavOptions
);
} else {
return createStackNavigator(
{
Signup: {screen: Signup},
Verif1: {screen: Verif1},
}
);
};
};
createDynamicRoutes = () => {
return createAppContainer(
createBottomTabNavigator(this.bottomTabScreen())
);
};
render() {
const AppContainer = this.createDynamicRoutes();
return <AppContainer />;
}
};
const InitialNavigator = createSwitchNavigator({
Splash: SplashScreen,
App: App,
});
export default createAppContainer(InitialNavigator);
But the declaration of class App extends React.Component {...}
shows the error in IDE VS Code:
Cannot use property `Component` [1] with fewer than 1 type argument.Flow(InferError)
react.js(26, 30): [1] property `Component`
Quick Fix...
Peek Problem
Based on my reading of latest document for react native 0.59, class App extends React.Component {...} is fine. Version of React navigation is 3.9.
Looks like flow validation error, add empty props to your component.\
type Props = {};
export default class App extends Component<Props> {
...
}

I have a problem in my code when updating react-navigation v2 to v3

I want to update the react-navigation library V2 to V3 and change part of my code thinking that there would not be any problems but it turns out that I have problems creating createStackNavigator with a screen of type createDrawerNavigator and that in turn contains createBottomTabNavigator.
my code that works with the previous version was:
export const createRootNavigator = (signedIn = false) => {
const commonNavigationOptions = {
headerStyle: {
shadowColor: 'transparent',
elevation: 0
},
headerTintColor: DEFAULT_THEME.topaz
};
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer('Home'),
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.backgrouncolorHomeSafeArea)
})
},
Cards: {
screen: Tabs('Cards'),
navigationOptions: () => ({
headerStyle: {
height: 0
}
})
},
);
const SignedOut = createStackNavigator(
{
SignIn: {
screen: LoginContainer,
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.dark)
})
},
SelectableCardsList: { screen: SelectableCardsListComponent },
);
return createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
);
};
const Drawer = (initialRoute) => createDrawerNavigator(
{
Home: { screen: Tabs('Home') },
{
initialRouteName: initialRoute,
contentComponent: CustomDrawerComponent
}
);
const Tabs = (initialRouteName) => createBottomTabNavigator(
{
Home: {
screen: HomeContainer,
navigationOptions: {
tabBarLabel: I18n.t('tabs.me')
}
},
Home2: {
screen: Home2,
navigationOptions: {
tabBarLabel: I18n.t('tabs.credentials')
}
},
{
initialRouteName: initialRouteName,
tabBarComponent: ({ navigation }) => <CustomBottomBarComponent navigation={navigation} navigationState={navigation['state']} />,
tabBarOptions: {
style: {
backgroundColor: 'white'
}
}
}
);
try this solution with react-navigation V3 and send me an error
I try the following:
encapsulate createSwitchNavigator in createAppContainer and separate (SignedOut and SignedOut) createStackNavigator out of createSwitchNavigator, the rest is still the same.
export const createRootNavigator = (signedIn = false) => createAppContainer(createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
));
I get the following error: The component for route 'Home' must be a react component. For Example
import MyScreen from './MyScreen';
...
Home : MyScreen,
}
you can also use a navigator:
The problem is located in this part:
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer,
Also try to change Drawer for any component (a component with a blank screen) and this works, but I can not insert the Tabs in the Drawer.
Thank you very much for your help.

Perspective Animation Drawer with React Native?

I want to create perspective animation like following -
I am using react-native-scaling-drawer & have currently done -
My App.js is the root file which is as follows -
App.js
const AppNavigator = StackNavigator(
{
walkthroughStack: {
screen: WalkthroughStack,
},
drawerStack: {
screen: DrawerStack,
},
},
{
initialRouteName: 'walkthroughStack',
headerMode: 'none',
},
);
export default AppNavigator;
My Walkthrough.js file is the file which shows the Walkthrough of the app & is as follows -
WalkthroughStack.js
const WalkthroughStack = StackNavigator(
{
Walkthrough: {
screen: Walkthrough,
},
},
{
headerMode: 'none',
navigationOptions: {
headerVisible: false,
},
initialRouteName: 'Walkthrough',
},
);
export default WalkthroughStack;
My DrawerStack.js is the file which has the animation shown in the repo -
DrawerStack.js
let defaultScalingDrawerConfig = {
scalingFactor: 0.6,
minimizeFactor: 0.6,
swipeOffset: 20
};
class CustomDrawerView extends Component {
constructor(props) {
super(props);
}
componentWillReceiveProps(nextProps) {
/** Active Drawer Swipe **/
if (nextProps.navigation.state.index === 0)
this._drawer.blockSwipeAbleDrawer(false);
if (nextProps.navigation.state.index === 0 && this.props.navigation.state.index === 0) {
this._drawer.blockSwipeAbleDrawer(false);
this._drawer.close();
}
/** Block Drawer Swipe **/
if (nextProps.navigation.state.index > 0) {
this._drawer.blockSwipeAbleDrawer(true);
}
}
setDynamicDrawerValue = (type, value) => {
defaultScalingDrawerConfig[type] = value;
/** forceUpdate show drawer dynamic scaling example **/
this.forceUpdate();
};
render() {
const {routes, index} = this.props.navigation.state;
const ActiveScreen = this.props.router.getComponentForState(this.props.navigation.state);
return (
<ScalingDrawer
ref={ref => this._drawer = ref}
content={<LeftMenu navigation={this.props.navigation}/>}
{...defaultScalingDrawerConfig}
onClose={() => console.log('close')}
onOpen={() => console.log('open')}
>
<ActiveScreen
navigation={addNavigationHelpers({
...this.props.navigation,
state: routes[index],
openDrawer: () => this._drawer.open(),
})}
dynamicDrawerValue={ (type, val) => this.setDynamicDrawerValue(type, val) }
/>
</ScalingDrawer>
)
}
}
const AppNavigator = StackRouter({
{
Main: {
screen: Main,
},
Walkthrough: {
screen: Walkthrough,
},
Typography: {
screen: Typography,
},
},
{
headerMode: 'none',
gesturesEnabled: false,
navigationOptions: {
headerVisible: false,
},
initialRouteName: 'Main',
},
);
const CustomDrawer = createNavigationContainer(createNavigator(AppNavigator)(CustomDrawerView));
export default CustomDrawer;
But this isn't working as shown in the README of the repo. How to do it ?
Example given works with React-Navigation's v1. Make sure you are using v1 for react-navigation

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
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
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:
And then after I close the drawer, the new warning appears like this:
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.
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.
this.props.navigation.state.isDrawerOpen => true/false
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