React-router: state.action === 'pop' on clicking browser back button and forward button.

Created on 18 May 2015  路  9Comments  路  Source: ReactTraining/react-router

I need to apply different slide transitions when user moves forward and backward using navigation buttons in the browser. On clicking forward as well as backward button, the state.action is "pop". So, How can I distinguish between these two?

Router.run(routes, Router.HistoryLocation, function(Root, state){
  if (state.action === 'pop') {
    AppActions.browserBackClick();
  }
  React.render(<Root/>, document.getElementById('main'));
});

All 9 comments

@melwinVincent if you don't need in/out effects based on ReactCSSTransitionGroup, you can add animation classes on components; they will be active when components will mount. i use this aproach here

@cmnstmntmn On clicking the back button inside application, I'm changing the transitionName to ''slideBack". I need the same effect on clicking the browser back button also. For that, I'm using the below piece of code.

Router.run(routes, Router.HistoryLocation, function(Root, state){
    if (state.action === 'pop') {       
         AppActions.setNextTransition('slideBack');
    }
  React.render(<Root/>, document.getElementById('main'));
  AppActions.resetTransitions();
});

Now on clicking the browser back button as well as application back button the page slides from left to right.

But on clicking the browser forward button the sliding effect is from left to right, which is the sliding effect expected on clicking the back button.This is because the state.action is 'pop' on clicking back button as well as forward button of the browser and I'm calling an action to change the transitionName in the store when state.action is 'pop'.

I hope you understood the problem. Can you please suggest a possible solution for this.

I need to apply different slide transitions when user moves forward and backward using navigation buttons in the browser.

Sadly, you can't. I was working on something that would enable this (https://github.com/rackt/react-router/pull/843), but it seemed impossible to get 100% right due to browser API restrictions and inconsistencies, e.g. anchor (hash) navigation or refresh button would break it, at least on some major browsers.

I guess we could still try to make it happen and document all edge cases if there's a high demand.

Edit: Btw, it's not a bug that clicking the browser forward button results in a "pop" action. The wording is based on the History API's popstateevent, which is triggered whenever the current history item changes, unless replaceState or pushState was used.

Unfortunately we can't tell you reliably whether the forward or back button was clicked due to browser API limitations. As @taurose mentioned, the word pop is used for both events intentionally. push and replace are reserved for pushing a new entry onto the history stack and replacing the current item, respectively.

In version 1.0 of the API (current beta) you can attach properties to location.state based on a link/button click. This could help you determine which type of animation to do, for example. But you'd still want to have a default in place in case the user navigates using the back/forward buttons.

@mjackson Thats what me and @melwinVincent already implemented. We keep a copy of history in store and listen to pop action and set URL in store by comparing previous URLs.

Router.run(routes, Router.HistoryLocation, function(Root, state){

    if (state.action === 'pop') {
        AppActions.setNextTransition(state.path);
    }else{
        AppActions.nextRoute(state.path);
    }

      React.render(<Root/>, document.getElementById('main'));
      AppActions.resetTransition();
    }); 

AppActions.nextRoute(state.path) will just push the coming url into store by comparing with previous values. and AppActions.setNextTransition(state.path); contains algorith to detect forward or backward button is pressed.This seems to work for our usecase.

I'm having this same issue and using v2.0 what is the best recommendation? It seems like this is a pretty core issue for transitions. If I could ask the router where they came from it would be really helpful

In V2 Router.run is deprecated. I'm doing something like this in my top level component constructor:

  this.context.router.listenBefore(location => {

      let newDepth = this.getPathDepth(location.pathname);
      let currentDepth = this.getPathDepth(this.currentPath);

      // detect backbutton press
      if(location.action == "POP" && newDepth <= currentDepth) {
        this.backBackButtonAction = true;
      } else {
        this.backBackButtonAction = false;
      }
     });
getPathDepth(path) {
      let pathElementsUnfiltered = path.split("/");
      let pathElements = pathElementsUnfiltered.filter(function(el){
        return el !== '';
      });
      return pathElements.length;
  }

And to get access to the router in your react component:

YourComponent.contextTypes = {
    router: React.PropTypes.object.isRequired
  };

I was able to achieve this by keeping track of location.keys whenever they changed. We know a pop action is back if the second to last key in the tracked history state is equal to the new key: https://github.com/ReactTraining/history/issues/334#issuecomment-322740342

I could be missing some edge cases though. Thoughts?

Unfortunately we can't tell you reliably whether the forward or back button was clicked due to browser API limitations. As @taurose mentioned, the word pop is used for both events intentionally. push and replace are reserved for pushing a new entry onto the history stack and replacing the current item, respectively.

That's too bad. Though understandable.

Is there a standardized way of tracking state this that is documented for React Router?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

misterwilliam picture misterwilliam  路  3Comments

ackvf picture ackvf  路  3Comments

yormi picture yormi  路  3Comments

wzup picture wzup  路  3Comments

maier-stefan picture maier-stefan  路  3Comments