Oursky Code
  • AI
  • Auth
  • More from Oursky
    • Oursky Business Blog
    • GitHub
    • About Us
  • AI
  • Auth
  • More from Oursky
0
Subscribe
Oursky Code
Oursky Code
  • AI
  • Auth
  • More from Oursky
    • Oursky Business Blog
    • GitHub
    • About Us
  • React Native

Updating React Navigation’s navigation bar title with a scene object

  • October 20, 2017
  • No comments
  • 3 minute read
  • YinYin Chiu
Total
0
Shares
0
0
0

NOTE: This version is based on React Navigation 1.0.0-beta.6

After moving to React Native Navigation

In a previous post, we shared how we restructured an app with React Navigation. We loved how Navigation could route and transit our app flow. We resolved our previous problem with Navigator, which suffered performance problems once more operations were added to the JS Thread. But then we encountered another issue with React Navigation’s rendering of our navigation bar. In this piece below, we outline how we introduced the scene object to update the navigation bar content.

Unlike Navigator, React Navigation provides a way that allows a scene to update its navigation bar content. Before using React Navigation, we used to have a route object to hold the render logic of the scene and its navigation bar. Here’s an example below:

const sampleRoute = {
  renderNavBarLeft: () => (<NavBarBackButton />),
  renderNavBarTitle: () => (<SimplePageTitle />),
  renderNavBarRight: () => null,
  renderScene: () => (<SamplePage />),
};

// navigator
<Navigator
  { ...otherPropsPassingToNavigator }
  renderScene={route => route.renderScene()}
  navigationBar={
    <Navigator.NavigationBar
      routeMapper={{
        LeftButton: route => route.renderNavBarLeft(),
        RightButton: route => route.renderNavBarTitle(),
        Title: route => route.renderNavBarTitle(),
      }}
    />
  }
/>

Problem when updating the navigation bar with push animation

Now, it’s hard for us to update the navigation bar content in the scene component because they were rendered separately. The transition animation and update title for the navigation bar are performed as sequential actions. So the bar will not be rendered correctly before the view is pushed in.

Updating the title with setParams

We managed to resolve this problem by putting all the view states for the navigation bar into redux state. React Navigation provides a similar but simpler way to configure the scene’s navigation bar. Instead of using a route object, React Navigation allows us to define the configuration of navigation bar in the scene components:

class UserPage extends React.Component {

  static navigationOptions: {
    header: ({state}) => ({
      left: (<NavBarBackButton />),
      title: (
        <UserPageTitle 
          userName={state.params.userName}
          profilePicUrl={state.params.profilePicUrl}
        />
      ),
      right: (<UserPageRight />),
    }),
  };

  updateNavBarTitle() {
    this.props.navigation.setParams({
      userName: 'Yin',
      profilePicUrl: 'http://www.example.com/myProfilePic.png',
    });
  }
  ...
}

We then update the navigation bar content by calling setParams provided. Despite the fact that it is much easier to use, there are downsides to configuring the scene’s navigation bar like that.

One of problems is your navigation bar may display the wrong content for a few milliseconds. Any navigation state update (navigation state is not shallow equal) will cause React Navigation to perform a transition. Calling setParams will update the navigation state and, hence, make React Navigation perform a transition. However, it only allows one view transition at one time and it only passes a new navigation state to the scene or navigation bar components after the transition is finished. Therefore, if you try to update your navigation bar in the componentWillMount of the scene component, your navigation bar may display the wrong content for a few milliseconds.

react navigation bar demo
The flashing navigation bar title

The Scene object comes in place to rescue

To prevent the above problem, we implemented a Scene object. The Scene object has the ability to call a forceUpdate navigation bar component and provide an interface for the scene component to update the props passing to the navigation bar component.

With this setup, we can just call this.props.scene.title.setState(newStateForNavBarTitle) in our scene component to update the state of navigation bar title component.

function Scene() {
  const forceUpdates = {};
  const partStates = {
    left: {},
    right: {},
    title: {},
  };

  function closeOverThisComponent(part, C) {
    class WrappedComponent extends Component {
      constructor(...args) {
        super(...args);
        forceUpdates = this.forceUpdate.bind(this); // Force update
      }
      componentWillUnmount() {
        delete forceUpdates;
      }
      render() {
        const props = {
          ...this.props,
          ...partStates,
       };
        return (
          <C {...props} />
        );
      }
    }
    return WrappedComponent;
  }

  function setPartState(p, payload) {
    partStates = {
      ...partStates,
      ...payload,
    };
    forceUpdates && forceUpdates();
  }

  const title = closeOverThisComponent('title', DummyNavBarComponent);
  const right = closeOverThisComponent('right', DummyNavBarComponent);
  const center = closeOverThisComponent('center', DummyNavBarComponent);
  return {
    left: {
      Component: left,
      setState: setPartState.bind(undefined, 'left'),
    },
    right: {
      Component: right,
      setState: setPartState.bind(undefined, 'right'),
    },
    title: {
      Component: title,
      setState: setPartState.bind(undefined, 'title'),
    },
  };
}

Since we have written our own navigation state reducer, we can easily inject a Scene object into a new route.

To make it more easy to update – we wrap it up with the enhanceScene HOC (High-order Component). Now we can easily configure the header option in navigationOptions and inject the setState methods for the navigation bar into our Scene.

function enhanceScene(Scene) {
  class NavigationScene extends React.Component {

    static navigationOptions = {
      header: ({state}) => ({
        left: (
          <state.scene.left.Component />
        ),
        title: (
          <state.scene.title.Component />
        ),
        right: (
          <state.scene.right.Component />
        ),
      });
    };

    render() {
      return (
        <Scene
          { ...this.props.navigation.state }
        />
      );
    }
  }

  ...
}

Now we have a correct title in the navigation bar

React navigation bar web development

We now have a tricky work around to the problem of displaying the title bar. Viola!

Building an app? Our free developer tools and open source backend will make your job easier.

Read more from Oursky

Total
0
Shares
Share 0
Tweet 0
Pin it 0
Related Topics
  • javascript
  • mvc
  • React Native
  • react native route
YinYin Chiu

Previous Article
iphone X ios 11
  • iOS

Preparations you may have missed before submitting an iOS 11 app

  • October 9, 2017
  • David Ng
View Post
Next Article
skypad skygear side project
  • Web

How I wrote a HackerNews top page app in 2 hours

  • October 25, 2017
  • David Ng
View Post
You May Also Like
View Post
  • React Native

How we restructured our app with React Navigation

  • June 23, 2017
  • YinYin Chiu

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Subscribe Us

Join our mailing list to never miss an update!

Oursky Code Blog
A team of Developers, Designers and Geeks. All about hacking and building things.

Input your search keywords and press Enter.