History: in app history vs. browser history

Created on 13 Aug 2015  路  11Comments  路  Source: ReactTraining/history

Our app has an object detail page that has a back button. We want to set it up so if you visit the page from somewhere in our app the back button will take you back to that page. However if you visit the detail page directly the back button will take you to a default object list page. This currently working using the following:

    if (window.history.length > 1) {
        history.goBack();
    }

However, it does break down in the case that you come to the detail page from an external site. In this case the browser will have history and the back button will take you off our app where we would actually like it to take you to the default object list page.

Is it possible to have a history stack that is only the history within the context of the app that is using history? Basically the first time the app loads that page is treated as the first entry in the stack.

Most helpful comment

I just found myself needing this same functionality and I came up with the solution below. However I'm wondering if there are edge cases to relying on this.props.location.action?

if (this.props.location.action === 'PUSH') {
  this.props.history.goBack();
} else {
  this.props.history.pushState(null, '/videos');
}

All 11 comments

Is it possible to have a history stack that is only the history within the context of the app that is using history?

I would love to be able to provide history.length as part of our public API, but we have discussed this at length before, and I believe we came to the conclusion that it's not possible in browsers. Still, I'd love it if we could find a way.

Well, I've found that history.length in the app context is impossible because of the scenario where the user leaves the app: although we do get the unload event, we can't tell if any of our entries are removed (e.g. clicking external link) or still there (e.g. jumping back to a different website). For example, if the user browses from /a to /b to /c, goes back to /b and then leaves the app somehow, we can't possibly know if /c is still in the browser history or not.

history.current or location.current isn't affected by this though, but it wouldn't be perfectly reliable either (just like resetting pop transitions).

However, in your case it seems like you don't need that at all. Wouldn't it be enough to use location.state?

// navigate from list to details
history.pushState({ fromList: true }, path);

// back button
if (location.state.fromList) {
  history.goBack();
} else {
  history.pushState(...);
}

@chrisdrackett thoughts?

it would seem to me that in the above case, if the user navigates from /b to an external page that /c should no longer be in the history anyways. I guess my use case can be checked by somehow looking if the previous value in history is part of the local site or an external site, so something similar to what @taurose suggested is probably worth looking into.

if the user navigates from /b to an external page that /c should no longer be in the history anyways.

It will remain in browser history when using back or forward buttons to navigate to another page. If we were to provide history.length and the user came back to /b (again, using back/forward), a canGoForward based on history.length would misbehave.

Anyway, I don't think history.length is what you want, since it would stay the same when pressing the back button. It sounds like it should either be something like location.current > 0 (canGoBack) to figure out if there's something in your app to go back to, or location.state as I suggested (drawback being that it only works when using links, e.g. not when manually entering URL).

Is there anything to do here @chrisdrackett? If not, I'd like to close. Please be the steward of your own ticket :) Thanks!

I think there is a mismatch between how I'm thinking about history (I only care about the window where the user is on my app and not any history before or after that). Regardless, sounds like I can get this functionality with a little work on my end, so I'll close this.

I just found myself needing this same functionality and I came up with the solution below. However I'm wondering if there are edge cases to relying on this.props.location.action?

if (this.props.location.action === 'PUSH') {
  this.props.history.goBack();
} else {
  this.props.history.pushState(null, '/videos');
}

@nickw 's solution does not work of you go back 2 times in a row. I ended up using this (react-router uses this history library):

import {hashHistory} from 'react-router'

const previous = []

export const go = (path) => {
  previous.push(path)
  hashHistory.push(path)
}

export const goBack = () => {
  if (previous.length){
    hashHistory.goBack()
    previous.pop()
  } else {
    hashHistory.push('/')
  }
}

This way I prevent the back button from navigating outside of my own webpage and showing the homepage instead.

For anyone who was in my situation who was pointed here while using react-router-redux and react-router and had the same issue as OP. I think the main thing here is to forget about goBack and to pass information through the location state that can uniquely identify the route that triggered your route.

const {browserHistory} = require('react-router');
browserHistory.push({
  pathname: listItemPath,
  state: {fromList: true},
})
//In the component linked to by listItemPath
const mapStateToProps = ({routing}) => {
  return {
    fromList: routing.locationBeforeTransitions.state ?
               routing.locationBeforeTransitions.state.fromList
               : undefined,
  };
};

Not the best solution, but this might help others

const browserHistory = createBrowserHistory();

let previous = 0;
const _push = browserHistory.push;
const _goBack = browserHistory.goBack;

browserHistory.push = (path, state) => {
  previous += 1;
  _push(path, state);
};

browserHistory.goBack = () => {
  if (previous > 0) {
    previous -= 1;
    _goBack();
  } else {
    browserHistory.push('/');
  }
};
Was this page helpful?
0 / 5 - 0 ratings

Related issues

yangmingshan picture yangmingshan  路  6Comments

AndreasHogstrom picture AndreasHogstrom  路  4Comments

sidoruk-sv picture sidoruk-sv  路  3Comments

vass-david picture vass-david  路  8Comments

mgustin12 picture mgustin12  路  5Comments