Detect previous path in react router?
Asked Answered
E

15

121

I am using react router. I want to detect the previous page (within the same app) from where I am coming from. I have the router in my context. But, I don't see any properties like "previous path" or history on the router object. How do I do it?

Endowment answered 2/9, 2016 at 9:25 Comment(3)
Check this.props.history.location.pathname. It should work.Hallerson
hello , do you have any idea, how to get the previous page out of the app or web app?from where did the user visit the web app?can this be checked on reactjs?Bokbokhara
I answered how to do this in V6 here: https://mcmap.net/q/182729/-how-to-get-previous-url-in-react-router-v6Feria
H
35

You can save previous path in a componentWillReceiveProps lifecycle method. The logic is very close to the example provided in troubleshooting section of react-router docs.

<Route component={App}>
  {/* ... other routes */}
</Route>

const App = React.createClass({
  getInitialState() {
    return { prevPath: '' }
  },

  componentWillReceiveProps(nextProps) {
    if (nextProps.location !== this.props.location) {
      this.setState({ prevPath: this.props.location })
    }
  }
})

And lately, access it from the state.

Huskey answered 2/9, 2016 at 10:11 Comment(5)
Thanks. I pushed the state with path along with the pathname. Your solution is better.Endowment
Glad to help you!Huskey
It does not work for me. I use <Link to /> to navigate within my app and componentWillReceiveProps does not run when I click on any links in the component.Knar
I used componentWillUnmount to set the state but if I move to other component, I can not access the state unless I pass it to the component.Knar
@AlexandrLazarev What about functional components? Do you mind updating your answer? Thanks in advance.Initiative
B
98

You can pass down state using the <Link> component, in this case a pathname:

<Link to={{pathname: '/nextpath', state: { prevPath: location.pathname }}}>Example Link</Link>

You can then access prevPath from this.props.location.state in the next component

Bertrand answered 1/5, 2018 at 2:22 Comment(10)
You can also do the same with browserHistory.push()Paratrooper
Neat trick. However, there is a clear gotcha if you're not careful. Page A send a user to page B with state {pathname: pathB, state: { prevPath: pathA}}. When on page B and the user navigates to the previous page, passing state of similar structure, you can see the problem.Lugo
This redirects to the API host, which is running at port 3000, react app is on 3001. How to fix that?Ceramist
using state is problematic for me because it survives a refresh. i'm using it on a "thank you" page after filling a form. i don't want people to access this page if they didn't come from the origin form. or am i doing it wrong?Chimere
Yeah, with state is a very bad answerValidate
can someone please improve this answer by providing how to pass this to next component...he left that very important piece outRustice
@Rustice did you read beneath the block of code?Bertrand
yes i did, what does it take to actually provide an example of how this is passed down??? what does it cost to do that? one line of code? point is to make an answer as helpful as it can for people to find it useful enough to use...not useable as is right now..still unclearRustice
@Rustice sorry if it's not clear, you can access it on props.location.state.prevPathBertrand
API has changed for react router v6 - 'state' is now a prop of Link, and to access the location you go through useLocation. The state surviving a refresh is actually a great bonus, and makes for a coherent user experience, but you do need to be mindful of it.Millen
S
49

Instead of checking what the previous page is, approach the problem from a different angle. Pass the current page as props to the component or link that you're going to navigate to.

In the previous page or component that I'm calling history.push or clicking the link from, I add a state of the current page that I'm on e.g.

history.push(`/device/detail`, { from: 'device detail page' } );

I can then access what the previous page was using history.location.state.from

Surah answered 6/8, 2020 at 4:1 Comment(6)
Thanks! For some reason I couldn't understand where to find the from but thanks to you I found it. const history = useHistory(); console.log(history.location.state.from);Irretentive
It's just history.location.from not history.location.state.from @IrretentiveLuce
It should be history.location.state.from because history.location does not have a from property. So history.location.from should not exist. You can confirm this @LuceSurah
can you paste a more detail answer so one can benefit from your great answer? now it is not in a useful state as no example of how things tie together..anyone willing to improve this answer???Rustice
Your answer deserve a 100 up vote, thank you :DStoll
This works, but not for browser navigations. If the user clicks the back button we may not not exactly from which page or state he came.Driven
B
36

If you're using react-router-redux you can create a reducer which hooks into the events dispatched by react-router-redux.

export default function routerLocations(state = [], action) {
  switch (action.type) {
    case "@@router/LOCATION_CHANGE":
      return [...state, action.payload]
    default:
      return state;
  }
}
Benkley answered 31/5, 2017 at 14:55 Comment(3)
To add on top of that, you can import the action name import { LOCATION_CHANGE } from 'react-router-redux';Cesar
if you are using connected-react-router you can use import { LOCATION_CHANGE } from 'connected-react-router';Mcgean
@Mcgean it just print @@router/LOCATION_CHANGE on consoleLoats
H
35

You can save previous path in a componentWillReceiveProps lifecycle method. The logic is very close to the example provided in troubleshooting section of react-router docs.

<Route component={App}>
  {/* ... other routes */}
</Route>

const App = React.createClass({
  getInitialState() {
    return { prevPath: '' }
  },

  componentWillReceiveProps(nextProps) {
    if (nextProps.location !== this.props.location) {
      this.setState({ prevPath: this.props.location })
    }
  }
})

And lately, access it from the state.

Huskey answered 2/9, 2016 at 10:11 Comment(5)
Thanks. I pushed the state with path along with the pathname. Your solution is better.Endowment
Glad to help you!Huskey
It does not work for me. I use <Link to /> to navigate within my app and componentWillReceiveProps does not run when I click on any links in the component.Knar
I used componentWillUnmount to set the state but if I move to other component, I can not access the state unless I pass it to the component.Knar
@AlexandrLazarev What about functional components? Do you mind updating your answer? Thanks in advance.Initiative
A
14

Use useHistory hook of react-router to go to the previous path in stateless of functional component. For more information follow the link https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/hooks.md#useroutematch

import { useHistory } from "react-router-dom";

function demo () {
    let history = useHistory();
    const goToPreviousPath = () => {
        history.goBack()
    }
    return (
      <div>
        <Button
          onClick={goToPreviousPath}
        >
          Back
        </Button>
      </div>
    ):
}
Alt answered 29/1, 2020 at 9:10 Comment(4)
You should note that if you reload the page, however, the new previous path becomes the one you just reloaded. If you reload 2 times and then try history.goBack(), you will encounter the same page twice, which is not you usually want.Lugo
That's not the case now it's 2021. I believe they've updated it and they've already fixed that bug @Dev. I used the code above in my code and tried to reload the page many times but it works just fine.Marine
The problem with this approach is it keeps the </> browser buttons and the > becomes enabled. If you want to always be on the latest page (> disabled), but just display the previous screen as your latest page, this won't work.Vandiver
This provides a way to navigate backwards. But it does not provide a way to access the previous URL which is what the question askedLuce
D
9

You could listen and build a back stack using history.listen. Here's a hook that does just that.

import { Location } from 'history';
import { useEffect, useState } from 'react';
import { useHistory } from 'react-router';

const useBrowserBackStack = () => {
  const history = useHistory();
  const [backStack, setBackStack] = useState<Location[]>([]);
  useEffect(() => {
    history.listen((location, action) => {
      setBackStack(backStack => {
        switch (action) {
          case 'POP':
            return backStack.slice(0, backStack.length - 1);
          case 'PUSH':
            return [...backStack, location];
          case 'REPLACE':
            return [...backStack.slice(0, backStack.length - 1), location];
        }
      });
    });
  }, [setBackStack, history]);
  return backStack;
};

export default useBrowserBackStack;

Then use in your top level component like this

const backStack = useBrowserBackStack();
Depside answered 28/1, 2022 at 19:55 Comment(1)
Great solution - this is the most up-to-date answer at the time of writing.Tumefaction
G
8

As of 08/2022, for those using React Router v6, you can either use Navigate component, Link component or useNavigate hook to pass the previous url to the next component:

In the redirecting component:

// with Navigate component (same as with Link component):
const location = useLocation();
...
<Navigate to="/nextpath" state={ { from: location } } />
...


// with useNavigate hook:
const navigate = useNavigate();
const location = useLocation();
....
navigate("/nextpath", { state: { from: location } });
...

In the component you redirected to:

...
const location = useLocation();
let from = location.state?.from?.pathname;
...
Garotte answered 22/8, 2022 at 20:33 Comment(0)
A
4

If you are using the <Redirect /> component, you can add a from property that will be added into location.state in the component you redirects to.

// in the redirecting component
<Redirect
to={{
  pathname: '/login',
  state: { from: location }
}}
/>

//in the other component you redirected to
...
const { location } = props.location.state;
...
Aftertime answered 29/8, 2019 at 12:11 Comment(1)
can you please add better details about the "other component"? and how the props are being passed and being accessed? thanksRustice
N
4

Using context you can store the previous location pathname:

const RouterContext = React.createContext();

const RouterProvider = ({children}) => {
  const location = useLocation()
  const [route, setRoute] = useState({ //--> It can be replaced with useRef or localStorage
    to: location.pathname,
    from: location.pathname //--> previous pathname
  });

  useEffect(()=> {
    setRoute((prev)=> ({to: location.pathname, from: prev.to}) )
  }, [location]);
  
  return <RouterContext.Provider value={route}>
    {children}
  </RouterContext.Provider>
}

Then in some component under RouterProvider:

const route = useContext(RouterContext);
//...
<Link to={route.from}>
  Go Back
</Link>

Or

history.push(route.from);

Note: RouterContext should be under Router component and If you don't want to update the state you can use useRef instead. If you need more persistence use localStorage

Normand answered 20/3, 2021 at 20:4 Comment(0)
E
4

This answer uses a similar approach to @AlexandrLazarev, but implements it via React Hooks. This ensures that all changes to the path are captured regardless of how they are initiated. The previous path value is stored in the top level component's state which can then be passed down to children as props or if you're using a global state provider like Redux can be added to a store:

import { useEffect, useState } from 'react'

cont App = ({ location }) => {
  const [currentPath, setCurrentPath] = useState(null);
  const [previousPath, setPreviousPath] = useState(null);

  useEffect(() => {
    if (location.pathname !== currentPath) {
      setPreviousPath(currentPath);
      setCurrentPath(location.pathname);
    }
  }, [location.pathname]);
}

The implementation in the markup would look something like the below snippet. I've been using Reach Router, but given that its been merged with React Router it should work there as well. The Router component makes the location prop available to all of its children and holds the value of the current path under its pathname attribute

<Router>
  <App path="/*" />
<Router/>
Erickericka answered 10/5, 2021 at 21:9 Comment(0)
T
2

If it's help, see this solution if you don't want the component to re-render and still get the previous path..

location.js:

import { useLocation } from 'react-router-dom';


export default () => {
  const location = useLocation();
  const path = location.pathname;
  const store = window.localStorage;
  let url = '';
  let prevUrl = '';

  url = store.getItem('url');
  store.setItem('prevUrl', url);
  store.setItem('url', path);

  url = store.getItem('url');
  prevUrl = store.getItem('prevUrl');

  return { url, prevUrl };

}

In other file

import Location from './location.js'


const { url, prevUrl } = Location()
Tachistoscope answered 28/4, 2020 at 12:23 Comment(1)
just import it where you want and do const { url, prevUrl } = importedFunc(); Be carefull in SSR : the window object is undefined.Tachistoscope
F
-1

React - Get previous path using props

console.log(props.history.location.state && props.history.location.state.from.pathname);

if you redirect using <Link> OR <Redirect> ? pathname : undefined

Frustrate answered 19/5, 2020 at 18:44 Comment(0)
R
-2

For those looking how to navigate to n pages backwards or forwards, with react-router-v6 you can use the useNavigate API,

import {useNavigate} from 'react-router-dom'

const navigate = useNavigate()

Then you can pass a function to an onClick event on a button for example

<button onClick={() => navigate(-1)}>Previous</button>

note that negative integers are for backwards and positive for forwards.

For further reading, check out the docs : https://reactrouter.com/docs/en/v6/upgrading/v5#use-usenavigate-instead-of-usehistory

Revivalism answered 2/5, 2022 at 21:49 Comment(2)
The ask is "how do I get the previous path when I change to a new path", not "how do I go back". This is often used for things like tracking the user's navigation path (ie: "from which page did the user come from to get to the current page").Tennilletennis
oh thanks @Tennilletennis I didn't pay close attention to the question. but I was looking for the same question and I got here so I thought it might be helpful for someone.Revivalism
A
-3

I needed a way to conditionally navigate only if previous path equals a specific route. With a functional component it worked out like this. The && will fire the .push() method only if route is '/cart'.

import {useHistory} from "react-router-dom";

const history = useHistory();

history.location.pathname === '/cart' && history.push('/checkout');
Amphictyony answered 9/6, 2021 at 6:40 Comment(2)
"navigate only if previous path equals a specific route" But this - history.location.pathname is the current path.Jolley
The question is to identify the previous path, your answer will get the current path.Quartis
E
-3

Use the useNavigate() hook, e.g. const navigate = useNavigate();. Call the navigate() function passing it -1 - navigate(-1). Calling navigate with -1 is the same as hitting the back button.

const previousPageHandler = () => {
    navigate(-1);
}
Exurbanite answered 10/1, 2023 at 1:1 Comment(1)
This is totally unrelated to the question :) The question is about detecting the previous page, not about navigating to it, which is trivial.Ginny

© 2022 - 2024 — McMap. All rights reserved.