React Native Yet Another Navigator
Yet another react native navigator component.
Installation
First of all, this component uses awesome react-native-vector-icons, so you need to install it (it's simple)...
then,
npm install react-native-ya-navigator --save
Usage
YANavigator component
import YANavigator from 'react-native-ya-navigator';
class App extends React.Component {
render() {
return (
<YANavigator
initialRoute={{
component: MyScene,
}}
navBarStyle={{
backgroundColor: 'green',
}}
/>
)
}
}
YANavigator propTypes:
style
navBarStyle
sceneStyle
initialRoute
initialRouteStack
defaultSceneConfig
(default value is Navigator.SceneConfigs.PushFromRight foriOS
and Navigator.SceneConfigs.FadeAndroid forAndroid
).useNavigationBar
(useful if you want to render your navBar component on each scene (ToolbarAndroid for example) instead of the embedded navBar)navBarUnderlay
(the view that will be rendered under all navBar items (react-native-blur for example))navBarBackBtn
icon
textStyle
eachSceneProps
(these props will be passed to each scene, for example, if you are using YANavigator inside tabs, you can to pass 'selected' prop to each scene, so each scene can decide should it updated via shouldComponentUpdate if it was hidden)customEventedProps
(you can pass here array of prop names that you need for link your custom components rendered in navigation bar)navBarFixedHeight
(use this to set custom fixed nav bar height)navBarCrossPlatformUI
(this prop means that title on android will be in center)
Also YANavigator
class has static property navBarHeight
(you can use it in your styles)
Navigation bar configuration in a scene
Your scene component should define static
property navigationDelegate
class MyScene extends React.Component {
render() {
return <View>{this.props.children}</View>
}
static navigationDelegate = {
/**
* if you want to listen nav bar items press events
* you must to provide id key
* @type {Something unique}
*/
id: 'myScene',
sceneConfig: myCustomSceneConfig,
/**
* false by default
* @type {bool}
*/
navBarIsHidden: true|false,
/**
* @type {String}
*/
navBarBackgroundColor: 'red',
/**
* @param {object} props [route props]
* @return {Class|JSX}
*/
renderTitle(props) {
return MyTitleComponent
// or
return <MyTitleComponent title={props.title || 'Title'}/>
},
/**
* @param {object} props [route props]
* @return {Class|JSX}
*/
renderNavBarLeftPart(props) {
return MyButtonComponent
// or
return <MyButtonComponent {...props}/>
},
/**
* @param {object} props [route props]
* @return {ReactElement|Object}
*/
renderNavBarRightPart(props) {
return MyButtonComponent
// or
return <MyButtonComponent {...props}/>
},
/**
* will be called first on back android button press
* @param {object} navigator [navigator instance]
*/
onAndroidBackPress(navigator) {
navigator.popToPop();
}
/**
* If it's true, 'onNavBarBackBtnPress' method will be called on backBtnPress instead
* of navigator.pop()
* false by default
* @type {bool}
*/
overrideBackBtnPress: true|false,
/**
* Tint color of backBtn (applies to icon and text)
* @type {String}
*/
navBarBackBtnColor: 'white',
}
}
Listening navigation bar items events
You should wrap your scene component with YANavigator.Scene
component and set this to delegate
prop.
Don't forget to define id
in the navigationDelegate
class MyScene extends React.Component {
render() {
return (
<YANavigator.Scene
delegate={this}>
{this.props.children}
</YANavigator.Scene>
)
}
Also YANavigator.Scene
has style
prop and paddingTop
(if it's true(default value) then scene will have top padding equals height of the navigation bar, also you can use YANavigator.navBarHeight
in your styles)
And one more thing... ;-)
You can listen when a scene will lose focus via route prop onSceneWillBlur
...
onLinkPress = (link) => {
tabBar.hide(),
this.props.navigator.push({
component: Browser,
props: {
url: link,
/**
* @param {Boolean} true means the scene was popped, false means a new scene was pushed
*/
onSceneWillBlur: (isBack) => tabBar.show(),
},
})
}
...
How to handle navigation bar items events?
There are a few simple rules
- if you pass as navBar item just a
class
, it should havepropTypes
with prop that you want to listen, then you should define method that will be called (onNavBarTitlePress
,onNavBarLeftPartPress
,onNavBarRightPartPress
,onNavBarTitleChange
,onNavBarTitleValueChange
, etc...) - if you pass as navBar item
JSX
, then props that you want to listen should return just a string - name of the delegate method that will be called - currently supported props
onPress
,onChange
,onValueChange
,onSelection
,onBlur
,onFocus
,onSelectionChange
,onSubmitEditing
class MyNavBarTitle extends React.Component {
render() {
return (
<TouchableOpacity onPress={this.props.onPress}>
<Text>{'Default Text'}</Text>
</TouchableOpacity>
)
}
static propTypes = {
onPress: React.PropTypes.func, // required
}
}
class MyScene extends React.Component {
onNavBarTitlePress(e) {
// press event
console.log(e)
}
onFirstBtnPress(e) {
alert('Right side - first btn press');
}
onSecondBtnPress(e) {
alert('Right side - second btn press');
}
onSceneWillFocus() {
console.log('Scene will focus');
}
onSceneDidFocus() {
console.log('Scene did focus');
}
render() {
return (
<YANavigator.Scene
delegate={this}>
{this.props.children}
</YANavigator.Scene>
)
}
static navigationDelegate = {
id: 'myScene',
renderTitle() {
return MyNavBarTitle;
},
renderNavBarLeftPart() {
return (
<View style={{flexDirection: 'row'}}>
<TouchableOpacity onPress={() => 'onFirstBtnPress'}>
<Text style={{fontSize: 16, paddingLeft: 20, color: '#fff'}}>{'1'}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => 'onSecondBtnPress'}>
<Text style={{fontSize: 16, paddingLeft: 20, color: '#fff'}}>{'2'}</Text>
</TouchableOpacity>
</View>
)
}
}
How to change navigation bar items dynamically?
There are two options:
-
Each scene can access to navBar items via
ref
and modify its state using standardsetState
method, or can call other methods provided by your component.ref generated from the template
const ref = `${navigationDelegate.id || `${navigator.state.presentedIndex + 1}_scene`}_leftPart|rightPart|title`; // usage this.props.navigator._navBar.refs['navDelegateId_rightPart'].doSmth() // or if navigationDelegate id is not defined this.props.navigator._navBar.refs['1_scene_rightPart'].doSmth()
-
If you want re-render your navBar component with new props or just re-render, then you should use navBar's
updateUI
method
Also NavBar component has some helpful methods
show
('fade'|'slide') default behavior isfade
hide
('fade'|'slide') default behavior isfade
class MyNavBarTitle extends React.Component {
constructor(props) {
super(props)
this.state = {
text: props.text
}
}
render() {
return (
<TouchableOpacity onPress={this.props.onPress}>
<Text>{this.state.text}</Text>
</TouchableOpacity>
)
}
static propTypes = {
onPress: React.PropTypes.func,
text: React.PropTypes.string,
}
static defualtProps = {
text: 'Default Text',
}
}
class MyScene extends React.Component {
onBtnPress() {
this.props.navigator._navBar
updateUI({
title: <MyNavBarTitle text={'Re rendered'}>,
rightPart: (
<TouchableOpacity onPress={() => 'onBtnPress'}>
<Text style={{fontSize: 12}}>{'Updated btn'}</Text>
</TouchableOpacity>
),
// leftPart: <MyAwesomeBtn text={'left'}/>,
})
}
onNavBarTitlePress() {
this.props.navigator._navBar.refs['myScene_title'].setState({
text: 'Other title',
})
}
render() {
return (
<YANavigator.Scene
delegate={this}>
{this.props.children}
</YANavigator.Scene>
)
}
static navigationDelegate = {
id: 'myScene',
renderTitle() {
return MyNavBarTitle;
},
renderNavBarRightPart() {
return (
<TouchableOpacity onPress={() => 'onBtnPress'}>
<Text style={{fontSize: 16, paddingLeft: 20, color: '#fff'}}>{'Btn'}</Text>
</TouchableOpacity>
)
}
}