Here, I'll show you how to set up the Navigator component to work as simply as NavigatorIOS. I think the React Native documentation is lacking with regards to this.
NavigatorIOS
The NavigatorIOS component works out of the box. Simply stick it in your JSX:
// app/components/App.js
//...
var App = React.createClass({
render() {
return (
<NavigatorIOS
style={{flex: 1}}
initialRoute={{
title: 'Log In',
component: Login
}}/>
);
}
});
//...
And you're good to start pushing and popping routes:
// app/components/Login.js
//...
var Login = React.createClass({
pushToMain: function() {
this.props.navigator.push({
title: "Main",
component: Main,
passProps: {email: this.state.email}
});
},
//...
passProps
is passed down, and you even get a navigation bar for free. (The navigation bar can be hidden by giving the route object a property of navigationBarHidden
set to true
.)
Navigator
If you've looked at the React Native documentation for the Navigator component, you'll find things aren't so simple. Navigator is given a renderScene
prop which returns JSX to be rendered, in response to having a route pushed onto its stack. This is from the React Native documentation:
render() {
const routes = [
{title: 'First Scene', index: 0},
{title: 'Second Scene', index: 1},
];
return (
<Navigator
initialRoute={routes[0]}
initialRouteStack={routes}
renderScene={(route, navigator) =>
<TouchableHighlight onPress={() => {
if (route.index === 0) {
navigator.push(routes[1]);
} else {
navigator.pop();
}
}}>
<Text>Hello {route.title}!</Text>
</TouchableHighlight>
}
style={{padding: 100}}
/>
);
}
This is not pretty. However, by attaching components to our route objects, we can set this up to behave exactly as NavigatorIOS does:
// app/components/App.js
// ...
var routes = [
{
component: Login,
title: 'Log In'
}
];
var App = React.createClass({
//...
render() {
return (
<Navigator
initialRoute={routes[0]}
initialRouteStack={routes}
renderScene={(route, navigator) => {
return (
<route.component navigator={navigator} {...route.passProps}/>
)
}}
style={{flex: 1}}/>
);
}
//...
Now this will work in place of NavigatorIOS. One thing we need to address, however, is the lack of a navigation bar. You have to implement this on your own; I suggest creating a reusable Navbar component, like the following:
// app/components/shared/Navbar.js
import React from 'react';
import Icon from 'react-native-vector-icons/Octicons'
import {
StyleSheet,
Text,
View,
TouchableOpacity
} from 'react-native';
var Navbar = React.createClass({
onLeftButtonPress() {
this.props.onLeftButtonPress();
},
onRightButtonPress() {
this.props.onRightButtonPress();
},
render() {
var renderLeftButton = () => {
if (!this.props.leftButtonHidden) {
return (
<TouchableOpacity onPress={this.onLeftButtonPress}>
<Icon name={this.props.leftIconName} size={15} color="white" style={{padding:10}}/>
</TouchableOpacity>
)
} else {
return (
<TouchableOpacity onPress={this.onLeftButtonPress}>
<Icon name="search" size={15} color="rgba(0,0,0,0)" style={{padding:10}}/>
</TouchableOpacity>
)
}
}
var renderRightButton = () => {
if (!this.props.rightButtonHidden) {
return (
<TouchableOpacity onPress={this.onRightButtonPress}>
<Icon name={this.props.rightIconName} size={15} color="white" style={{padding:10}}/>
</TouchableOpacity>
)
} else {
return (
<TouchableOpacity onPress={this.onRightButtonPress}>
<Icon name="search" size={15} color="rgba(0,0,0,0)" style={{padding:10}}/>
</TouchableOpacity>
)
}
}
return (
<View style={styles.topBar}>
{renderLeftButton()}
<Text style={styles.title}>
{this.props.title.toUpperCase()}
</Text>
{renderRightButton()}
</View>
);
}
});
const styles = StyleSheet.create({
topBar: {
padding: 8,
flexDirection: 'row',
justifyContent: 'space-between',
paddingTop: 24,
backgroundColor: '#2dbecd',
alignItems: 'center',
height: 90,
},
title: {
color: 'white',
fontFamily: 'Helvetica',
fontSize: 18,
}
});
module.exports = Navbar;
Which can be used as follows:
// app/components/Main.js
//...
render() {
return (
<View style={styles.container}>
<Navbar
onLeftButtonPress={() => this.props.navigator.pop())}
title="Main"
rightButtonHidden={true}
leftIconName="chevron-left"/>
//...
One final caveat is that you'll want to give your navigation-level components (Login
, Main
, etc) background colours, or the previous route will be visible beneath them.