I found few posts that give some "hints" on how to integrate navigation\routes with react-native-side-menu, unfortunately didn't find any post that show full working example of such functionality.
I'm also not sure what is the simplest implementation for navigation\routes, and what's the difference between these 2 options (of course this is not specifically to side menu, but in my case should join together).
Can any one point to such example ?
The createStackNavigator function passes behind the scenes, a navigate prop to the HomeScreen and AboutScreen components. The navigate prop allows for navigation to a specified screen component. This is why we are able to use it on a button at HomeScreen.
React Native is an open-source JavaScript framework, designed for building apps on multiple platforms like iOS, Android, and also web applications, utilizing the very same code base. It is based on React, and it brings all its glory to mobile app development.
If you mean the drawer menu, check react-native-material-design and the given demo-app.
So eventually I was able to integrate standard Navigator as demonstrated in Facebook's official sample code:
https://github.com/facebook/react-native/tree/master/Examples/UIExplorer/Navigator
With react-native-side-menu component, and this is how the entire code looks like:
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var SideMenu = require('react-native-side-menu');
var {
Component,
Navigator,
AppRegistry,
View,
Text,
Image,
StyleSheet,
ScrollView,
TouchableOpacity
} = ReactNative;
class FirstPage extends Component {
render() {
return (
<View style={styles.page}><Text style={styles.pageContent}>First Page</Text></View>
);
}
}
class FirstPageMenu extends Component {
constructor(props) {
super(props);
this.state = {};
}
toggle() {
this.setState({
isOpen: !this.state.isOpen,
});
}
updateMenuState(isOpen) {
this.setState({ isOpen, });
}
onMenuItemSelected = (item) => {
this.setState({
isOpen: false,
selectedItem: item,
});
this.props.navigator.replace({ id: item });
}
render() {
const menu = <Menu onItemSelected={this.onMenuItemSelected} navigator={this.props.navigator}/>;
return (
<SideMenu
menu={menu}
isOpen={this.state.isOpen}
onChange={(isOpen) => this.updateMenuState(isOpen)}>
<MenuButton onPress={() => this.toggle()}/>
<FirstPage/>
</SideMenu>
);
}
};
class SecondPage extends Component {
...
}
class SecondPageMenu extends Component {
...
}
class ThirdPage extends Component {
...
}
class ThirdPageMenu extends Component {
...
}
class MenuNavigator extends Component {
constructor(props) {
super(props);
this._setNavigatorRef = this._setNavigatorRef.bind(this);
}
renderScene(route, nav) {
switch (route.id) {
case 'first':
return <FirstPageMenu navigator={nav} />;
case 'second':
return <SecondPageMenu navigator={nav} />;
case 'third':
return <ThirdPageMenu navigator={nav} />;
default:
return <FirstPageMenu navigator={nav} />;
}
}
render() {
return (
<Navigator
ref={this._setNavigatorRef}
initialRoute={{id: 'first'}}
renderScene={this.renderScene}
configureScene={(route) => {
if (route.sceneConfig) {
return route.sceneConfig;
}
return Navigator.SceneConfigs.FloatFromBottom;
}}
/>
);
}
componentWillUnmount() {
this._listeners && this._listeners.forEach(listener => listener.remove());
}
_setNavigatorRef(navigator) {
if (navigator !== this._navigator) {
this._navigator = navigator;
if (navigator) {
var callback = (event) => {
console.log(
`NavigatorMenu: event ${event.type}`,
{
route: JSON.stringify(event.data.route),
target: event.target,
type: event.type,
}
);
};
// Observe focus change events from the owner.
this._listeners = [
navigator.navigationContext.addListener('willfocus', callback),
navigator.navigationContext.addListener('didfocus', callback),
];
}
}
}
};
class MenuButton extends Component {
handlePress(e) {
if (this.props.onPress) {
this.props.onPress(e);
}
}
render() {
return (
<View style={styles.menuButton} >
<TouchableOpacity
onPress={this.handlePress.bind(this)}
style={this.props.style}>
<Text>{this.props.children}</Text>
<Image
source={{ uri: 'http://i.imgur.com/vKRaKDX.png', width: 40, height: 40, }} />
</TouchableOpacity>
</View>
);
}
}
class Menu extends Component {
static propTypes = {
onItemSelected: React.PropTypes.func.isRequired,
};
constructor(props) {
super(props);
}
render() {
return (
<ScrollView scrollsToTop={false} style={styles.menu}>
<Text
onPress={() => this.props.onItemSelected('first')}
style={styles.item}>
First
</Text>
<Text
onPress={() => this.props.onItemSelected('second')}
style={styles.item}>
Second
</Text>
<Text
onPress={() => this.props.onItemSelected('third')}
style={styles.item}>
Third
</Text>
</ScrollView>
);
}
};
var styles = StyleSheet.create({
menuButton: {
marginTop: 20,
backgroundColor: '#777'
},
menu: {
flex: 1,
width: window.width,
height: window.height,
padding: 20,
},
item: {
fontSize: 16,
fontWeight: '300',
paddingTop: 20,
},
page: {
flex: 1,
alignItems: 'center',
backgroundColor: '#777'
},
pageContent: {
flex: 1,
alignItems: 'center',
top: 200,
},
menu: {
flex: 1,
width: window.width,
height: window.height,
padding: 20,
},
item: {
fontSize: 16,
fontWeight: '300',
paddingTop: 20,
},
});
module.exports = MenuNavigator;
And index file should just point to Navigator:
const React = require('react-native');
const { AppRegistry, } = React;
const MenuNavigator = require('./SideMenuWithNavigation');
AppRegistry.registerComponent('MyApp', () => MenuNavigator);
react-native-side-menu
and navigator
.This starter also use redux (won't prevent from answering how to deal navigator + side menu).
When using sidemenu, the trick
for routing
is to replace previous route to prevent it from stacking (like it should in a common navigation):
navigate(route) {
const routeStack = [].concat(this.refs.navigator.getCurrentRoutes());
const previousRouteId = routeStack[routeStack.length - 1].id;
if (route.id !== previousRouteId) {
this.refs.navigator.replace(route);
}
if (this.state.sideMenuOpened) {
this.closeSideMenu();
}
}
Check my starter reactNativeReduxFastStarter
fast preview of code:
import React, {
Component
} from 'react';
import {
StyleSheet,
Text,
Dimensions,
Navigator,
StatusBar
} from 'react-native';
import SideMenu from 'react-native-side-menu';
import Icon from 'react-native-vector-icons/Ionicons';
import { AppRoutes } from '../../../common/config';
import {
SideMenuContent,
Button
} from '../../components';
import Home from '../home';
import AppState from '../appState';
const SCREEN_WIDTH = Dimensions.get('window').width;
class App extends Component {
constructor(props) {
super(props);
this.init();
}
init() {
this.state = {
sideMenuOpened: false
};
}
openSideMenu() {
this.setState({
sideMenuOpened : false
});
}
closeSideMenu() {
if (this.state.sideMenuOpened) {
this.setState({
sideMenuOpened : false
});
}
}
toggleSideMenu() {
this.setState({
sideMenuOpened: !this.state.sideMenuOpened
});
}
updateSideMenuState(isOpened) {
this.setState({
sideMenuOpened: isOpened
});
}
navigate(route) {
const routeStack = [].concat(this.refs.navigator.getCurrentRoutes());
const previousRouteId = routeStack[routeStack.length - 1].id;
if (route.id !== previousRouteId) {
this.refs.navigator.replace(route);
}
if (this.state.sideMenuOpened) {
this.closeSideMenu();
}
}
renderScene(route, navigator) {
switch (route.id) {
case 1:
const route1 = AppRoutes.getRouteFromRouteId(1);
return (
<Home
ref={route1.refView}
navigator={navigator}
navigate={(toRoute)=>this.navigate(toRoute)}
/>
);
case 2:
const route2 = AppRoutes.getRouteFromRouteId(2);
return (
<AppState
ref={route2.refView}
navigator={navigator}
navigate={(toRoute)=>this.navigate(toRoute)}
/>
);
default:
return (
<Home
ref={route1.refView}
navigator={navigator}
navigate={(toRoute)=>this.navigate(toRoute)}
/>
);
}
}
renderRouteMapper() {
const routes = AppRoutes.getAllRoutes();
return {
Title : (route, navigator, index, navState) => {
const currentRouteId = navState.routeStack[index].id;
return (
<Text style={styles.titleNavText}>
{routes[currentRouteId - 1].navbar.navBarTitle}
</Text>
);
},
LeftButton : (route, navigator, index, navState) => {
const currentRouteId = navState.routeStack[index].id;
return (
<Button
style={styles.leftNavButton}
onPress={(e)=>this.toggleSideMenu(e)
}>
<Icon
name={routes[currentRouteId - 1].navbar.navBarLeftIconName}
size={32}
color={'#333333'}
/>
</Button>
);
},
RightButton : (route, navigator, index, navState) => {
return null;
}
};
}
render() {
StatusBar.setBarStyle('light-content', true);
const DEFAULT_ROUTE = { id: 1, refView: 'HomeView' };
return (
<SideMenu
menu={<SideMenuContent
backGndColor="#ECECEC"
navigate={(route)=>this.navigate(route)}
/>}
isOpen={this.state.sideMenuOpened}
onChange={(isOpened) => this.updateSideMenuState(isOpened)}
bounceBackOnOverdraw={false}
openMenuOffset={SCREEN_WIDTH * 0.8}
>
<Navigator
ref="navigator"
initialRoute={ DEFAULT_ROUTE }
sceneStyle={ styles.navigator }
renderScene={(route, navigator)=>this.renderScene(route, navigator)}
configureScene={()=>Navigator.SceneConfigs.FadeAndroid}
navigationBar={
<Navigator.NavigationBar
routeMapper={this.renderRouteMapper()}
style={styles.navBar}
/>
}
/>
</SideMenu>
);
}
}
const styles = StyleSheet.create({
navigator: {
backgroundColor: '#fff',
borderLeftWidth: 0.5,
borderLeftColor: '#F1F1F1',
},
navBar: {
backgroundColor: '#fff',
borderWidth: 0.5,
borderColor: '#F1F1F1'
},
leftNavButton : {
flex : 1,
flexDirection : 'column',
alignItems : 'center',
marginTop : 4,
paddingTop : 0,
paddingBottom : 10,
paddingLeft : 20,
paddingRight : 10
},
rightNavButton : {
flex : 1,
flexDirection : 'column',
alignItems : 'center',
marginTop : 4,
paddingTop : 6,
paddingBottom : 10,
paddingLeft : 10,
paddingRight : 10
},
titleNavText : {
marginTop : 14,
color : '#333333'
}
});
export default App;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With