How to know if react-router can go back to display back button in react app
Asked Answered
N

6

38

There seems to be no API way to detect if you can go back from the current page in an app. One can check the history length, but that includes the forward and backward stack.

I want to display the back button only when the user actually can go back.

Nablus answered 23/5, 2016 at 7:57 Comment(0)
C
25

You can check location.key if you have a location key that means you routed in-app. But if you don't that means you come from outside of the app or you just open the app in a new tab etc.

import { useHistory, useLocation } from "react-router-dom";
const history = useHistory();
const location = useLocation();

<Button
  onClick={() => {
    if (location.key) {
      history.goBack();
    }
  }}
  disabled={!location.key}
/>;

For V6 and newer:

import { useHistory, useLocation } from "react-router-dom";
const history = useHistory();
const location = useLocation();

<Button
  onClick={() => {
    if (location.key !== 'default') {
      history.goBack();
    }
  }}
  disabled={location.key === 'default'}
/>;
Chimpanzee answered 30/12, 2021 at 14:15 Comment(5)
+1, This worked for me, although I wish I could put my hands on some documentation that stated plainly that a newly opened tab would always have a key of NULL.Phox
WARNING: With the latest version React Router V6 you have to check for "default" instead of an empty string. Now your initial location is "default" instread of "". Read API reference: github.com/remix-run/history/blob/main/docs/…Chastain
note location.key will be "default" if the user navigates via the URL bar. edge case but trueDawna
This is not working for me. localtion.key is not "default" when I open a subpage directly from the urlGasometer
doesn't work as expected if we navigate to a new tab using window.open() or <Link /> with target='_blank'. This way app still doesn't have where to go back but location.key isn't equal to 'default'Betancourt
G
12

You can do it by checking history.action if its value is POP, then there is no route to go back. That's the only way I found.

<Button
  onClick={() => {
    if (history.action !== 'POP') history.goBack();
  }}
  disabled={history.action === 'POP'}
/>
Gilburt answered 10/2, 2021 at 12:38 Comment(2)
The action property specifies the last action of the router, it doesn't have anything to do with the remaining items in the history stack.Bloodstained
If the user refreshes the page it will go back to "POP". So it won't work in that case.Elude
Z
3

I think I found a workaround

There is onpopstate event:

Calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event is only triggered by performing a browser action, such as clicking on the back button (or calling history.back() in JavaScript), when navigating between two history entries for the same document.

And I checked it: react router saves at state a key property: On history walking it may contains a data like: event.state: {key: "jr2go2", state: undefined}

And when we go back to last page on wich we entered the site or app, this state property will be null: event.state: null ,

So we can handle it with redux + rxjs (for example) like this:

// epic
export const handleHistoryWalkEpic = () =>
  fromEvent<PopStateEvent>(window, 'popstate')
  .pipe(
    map(event => setCanGoBack(!!event.state)),
  )

// reducer
case NavigationBarActionsTypes.SET_CAN_GO_BACK:
  return {
    ...state,
    canGoBack: action.payload.canGoBack,
  }

And you should set canGoBack to true on any push history action, it's possible with your any router handler component:

componentWillUpdate(nextProps: any) {
    let { location } = this.props;

    if (nextProps.history.action === 'PUSH') {
      this.props.setCanGoBack()
    }
}
Zaneta answered 17/10, 2019 at 13:23 Comment(0)
F
2
react-router 7.4.1 
react router-dom 6.21.1

Unfortunately, none of the solutions provided here were helpful for me. However, the following proved to be useful:

const canGoBack = window.history.state.idx !== 0
const canGoForward = window.history.state.idx < window.history.length - 1

<button disabled={!canGoBack}>go back</button>
<button disabled={!canGoForward}>go forward</button>
Flowering answered 24/1 at 9:11 Comment(0)
A
-1

You can do it by checking if location.history is > 2.

Example to show or not the back button in your app:

onBack={ history.length > 2 ? () => { history.goBack() } : null }
Affectation answered 18/7, 2019 at 16:44 Comment(5)
why 2 specifically?Thornton
@EdwardSammutAlessi if history length has 1 only that means that you don't navigate in the stack yet so you cant go back. Only if the stack have minimum 2 elements that means that you can go back in the stack navigation. Does make sense to you?Affectation
It is not a solution. Assume you've opened page #1 (history.length is 1), then you go to next page #2 (history.length is 2), then again - to #3 (history.length is 3). Now you go back from #3 to #1, history.length is still 3 but at page #1 you have nowhere to go back.Glomerule
I found if I open a new Chrome tab and goto a page history.length is always 2Northwestward
but if you go back, history.length doesn't decreaseOui
D
-4

it gives 'POP' and 'PUSH' value using this.props.location.action

Drud answered 3/9, 2016 at 6:13 Comment(1)
if we move back, it gives 'POP' action, that way you can detect itDrud

© 2022 - 2024 — McMap. All rights reserved.