Home

Awesome

The project is not maintained any more!!!

React Native Yet Another Navigator

preview_ios preview_android

Table of contents

Main goals

Dependencies

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:

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 {Class|JSX}
     */
    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

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:

  1. Each scene can access to navBar items via ref and modify its state using standard setState 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.navBarParts['navDelegateId_rightPart'].doSmth()
    // or if navigationDelegate id is not defined
    this.props.navigator.navBarParts['1_scene_rightPart'].doSmth()
    

  2. If you want re-render your navBar component with new props or just re-render use the template

YourComponentClass.navigationDelegate.renderTitle = () => // return component here with the new props

this.props.navigator.forceUpdateNavBar();

Also NavBar component has some helpful methods

this.props.navigator.showNavBar('slide');
this.props.navigator.hideNavBar('fade');
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() {
    MyScene.navigationDelegate.renderTitle = () => <MyNavBarTitle text={'Re rendered'} />
    MyScene.navigationDelegate.renderNavBarRightPart = () => (
      <TouchableOpacity onPress={() => 'onBtnPress'}>
        <Text style={{fontSize: 12}}>{'Updated btn'}</Text>
      </TouchableOpacity>
    );

    this.props.navigator.forceUpdateNavBar();
  }

  onNavBarTitlePress() {
    this.props.navigator.navBarParts.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>
      )
    }
  }

Feel free to go to example and explore it for more details

Contributing

Just submit a pull request!

Copyright and license

Code and documentation copyright 2015 Dmitriy Kolesnikov. Code released under the MIT license.