React-native-router-flux: How do I get the current route?

Created on 28 Jan 2016  路  34Comments  路  Source: aksonov/react-native-router-flux

I need to know what the current route is from the header component of a router. The issue is that the header needs to change depending on the current screen.

What is the best way to access the value of the current route globally?

I'm using redux and I'd like to have this value available in my store, but I'm not sure where to find it, and I'm not sure how to update the value when it changes.

Most helpful comment

For v4 you access currentRoute as Actons.currentScene

All 34 comments

You need to import { Actions } from 'react-native-router-flux'; and { this.props.title } should display the current route.

Thanks for the response @JeremDsgn. Can you please elaborate?

Actions contains a lot of properties, but I can't find one that reliably returns the current visible route.

this.props.title is not defined in my header component. The header component was added to the Router like this:

<Router header={Header}>
  • The Actions return all the data related to the routes you declare in index.ios.js.
  • this.props.title return the title value defined on the route itself.
<Route name="TabBar">
  <Router header={ header }>
    <Route name="page1" component={ Page1 } title="Page1" schema="tab" type="switch" /> // Page 1
    <Route name="page2" component={ Page2 } title="Page2" schema="tab" type="switch" /> // Page 2
    <Route name="page3" component={ Page3 } title="Page3" schema="tab" type="switch" /> // Page 3
  </Router>
</Route>

I'm sorry, I don't think my initial post was clear. I'll try to elaborate.

Here is my main component that contains the routes:

class App extends React.Component {
  render () {
    return (
      <Router header={Header}>
        <Route
          name="Index"
          component={Index}
          title="Index"
          initial={true} />

        <Route
          name="Detail"
          component={Detail}
          title="Detail" />
      </Router>
    );
  }
}

Here is the header component that is referenced in the above code:

class Header extends React.Component {
  render () {

    // from here, how do I know which page I'm looking at?
    if (???? === 'Index') {
      let title = 'this is the index';
    } else {
      let title = 'this is the detail';
    }

    return (
      <View>
        <Text>{title}</Text>
      </View>
    );
  }
}

What can I replace the ???? with in the above code that will let me know what to render in the Header component?

import React from 'react-native';
import { Actions } from 'react-native-router-flux';

class Header extends React.Component {
  render () {
    return (
      <View>
        <Text>{ `This is the ${ this.props.title }` }</Text>
        {/*
          This is the Index
          This is the Detail
        */}
      </View>
    );
  }
}

If I use that same code in my app, this.props.title is undefined.

Can you paste complete code of related page ?

My actual code is only a little more complex. I'm using redux, so the routes are wrapped in a Provider, and the Router that has the Header component is nested within a parent Router.

class App extends React.Component {
  render () {
    return (
      <Provider store={store}>
        <Router>
          <Route
            name="Login"
            component={Login}
            title="Login" />

          <Route name="SignedIn">
            <Router header={Header}>
              <Route
                name="Index"
                component={Index}
                title="Index"
                initial={true} />

              <Route
                name="Detail"
                component={Detail}
                title="Detail" />
            </Router>
          </Route>
        </Router>
      </Provider>
    );
  }
}

I think this might help.
You can get the current route using Actions.currentRouter.currentRoute during an Actions.onPop or Actions.onPush hook. This route has the name and any properties passed when defining your global route stack. The Actions can be accessed globally, so depending on what the current route is you can dispatch an action that updates the state in App. App is a component so modifying its state should trigger a re-render to show the updated header view.

Thanks @micksabox! Just to clarify, does this mean that in order to access the current route, I will need to add onPush, onPop, and onReplace hooks to each individual route in my app?

@lynndylanhurley glad to help! I know the hooks work on a Router instance, but I'm not sure about an individual Route. So add the hooks to each router that you care about, then during the on___ event hook you can query the current route.

This is what I would like to do:

// update the global redux store with the current route
function onEnterHandler() {
  let currentRoute = Actions.currentRouter.currentRoute;
  store.dispatch(updateCurrentRoute(currentRoute));
}

class App extends React.Component {
  render () {
    return (
      <Provider store={store}>
        <Router
          onPop={onEnterHandler}
          onReplace={onEnterHandler}
          onPush={onEnterHandler}>
          <Route
            name="Login"
            component={Login}
            title="Login" />

          <Route name="SignedIn">
            <Router header={Header}>
              <Route
                name="Index"
                component={Index}
                title="Index"
                initial={true} />

              <Route
                name="Detail"
                component={Detail}
                title="Detail" />
            </Router>
          </Route>
        </Router>
      </Provider>
    );
  }
}

The problem is that the onEnterHandler only gets called once when the page loads. So I end up with something like this:

class App extends React.Component {
  render () {
    return (
      <Provider store={store}>
        <Router
          onPop={onEnterHandler}
          onReplace={onEnterHandler}
          onPush={onEnterHandler}>
          <Route
            name="Login"
            component={Login}
            title="Login" />

          <Route name="SignedIn">
            <Router 
              onPop={onEnterHandler}
              onReplace={onEnterHandler}
              onPush={onEnterHandler}
              header={Header}>
              <Route
                name="Index"
                component={Index}
                title="Index"
                initial={true} />

              <Route
                name="Detail"
                component={Detail}
                title="Detail" />
            </Router>
          </Route>
        </Router>
      </Provider>
    );
  }
}

And so on for each child Router.

But this still doesn't work because if I navigate back to the Login route from one of the SignedIn routes, the onEnterHandler is not fired again.

In this case, it's actually easier and more reliable to update the current router state at the componentWillMount stage of the individual screen components.

But that defeats the purpose of using the router in the first place, which is to manage the routing from a central resource.

I feel like I'm missing something - is there a better way to do this?

@aksonov - would you accept a PR that introduces a way to access the current route?

@lynndylanhurley Did you find a way to update the redux store?

@lynndylanhurley what problem is with Actions.currentRouter.currentRoute ?

@aksonov - see #183.

The value seems to be correct when it's checked, but it doesn't trigger a state update.

For my use case, the style and content of my header component needs to change when I change routes. The header component is not re-rendered when the value of Actions.currentRouter.currentRoute changes.

@behl1 - the only way I can find to do this is to manually update the state when a route component is mounted. For example:

class Index extends React.Component {
  // ...
  componentDidMount () {
    this.props.dispatch(currentScreenChange('Index'));
  }

  // ...
}

Where currentScreenChange is a redux action that I created to set the router state manually.

I don't like this solution for the following reasons:

  • It involves a lot of code repetition.
  • It takes something that should be the router's responsibility and makes it the responsibility of every screen in the app.
  • It creates another weird thing that all of my co-workers and I must remember to do when creating new routes for our apps.
  • Even this method doesn't seem to get called every time. I'm still trying to nail down the issue, but I think sometimes when routes are popped, the destination component doesn't fire componentDidMount. I could be wrong about the cause, but the point is that even this method isn't 100% reliable.

I would prefer if there was a global callback for route changes that I could hook into.

Something like this would be ideal:

const store = // ... create redux store

// define a handler that is called whenever the current route is changed
function onEnterHandler(nextScreenName, lastScreenName) {

  // dispatch route change event from a single, centralized location
  store.dispatch(updateRoute(nextScreenName, lastScreenName);
}

// then in the render method, place the `onEnterHandler` on the top-level router:
<Router onEnter={onEnterHandler}>
  // ...
</Router>

@aksonov - how do you feel about this approach?

Thanks @aksonov - this works really well in all cases except for swipe navigation. I just submitted a PR to address this (see #204).

Thanks @aksonov!

I am using 'react-native-router-flux' to implement navigation between pages in my app.
There are some scenes (sort, filter) that I would like to remove from the back stack.

Is it possible to skip adding some pages to the stack or remove the page from back stack ?

The issue with removing a scene from the back stack was resolved by using popAndReplace' type in the the custom actions:
Actions.products({ ...this.props.product, type: 'popAndReplace' });

Can someone give more information about the Actions.currentRouter.currentRoute.
I'm using the v3.38.0 version and it is not existed in this package.

import { Actions } from 'react-native-router-flux';
console.log(Actions.currentRouter.currentRoute);

Error: undefined currentRoute. I searched for _'current'_ in the codebase, and I found nothing.

@lynndylanhurley I'm facing the same use case that you described and ended up with a solution like you mentioned in the first place:

class Index extends React.Component {
  // ...
  componentDidMount () {
    this.props.dispatch(currentScreenChange('Index'));
  }

  // ...
}

This works but it doesn't seem clean IMO (code repeating everywhere).

So I saw you suggest an improvement:

const store = // ... create redux store

// define a handler that is called whenever the current route is changed
function onEnterHandler(nextScreenName, lastScreenName) {

  // dispatch route change event from a single, centralized location
  store.dispatch(updateRoute(nextScreenName, lastScreenName);
}

// then in the render method, place the `onEnterHandler` on the top-level router:
<Router onEnter={onEnterHandler}>
  // ...
</Router>

I tried to implement that but I can't make it work: it seems onEnter is never fired. Can you give me a working example of this? Which version of RNRF do you use?

For v4 you access currentRoute as Actons.currentScene

@aksonov I saw that, but I read that v4 is still in beta. Is it safe to use it right now?

Yes, it has the same quality as ReactNavigation (that is still in beta too) - recommended way for navigation from Facebook

Even more we fixed some ReactNavigation issues already (like popTo, pass props to tabs, etc.)

Nice to hear that. Thanks for your quick answer!

@PumpkinSeed did u solve that?
I'm getting the same error

@sarahmarciano No, I tried an other way, but it's not related to this issue.

Which other way?
I'm searching a way to achieve that but didn't succeed

@sarahmarciano I had a problem, where the Actions.currentRouter.currentRoute was a small part, so I tried a totally other way to solve the whole problem, this is why I said _"but it's not related to this issue"_

Ok thank you
I'll try to solve it

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vinayr picture vinayr  路  3Comments

maphongba008 picture maphongba008  路  3Comments

sarovin picture sarovin  路  3Comments

fgrs picture fgrs  路  3Comments

sreejithr picture sreejithr  路  3Comments