Detect previous path in react router?

ReactjsReact Router

Reactjs Problem Overview


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?

Reactjs Solutions


Solution 1 - Reactjs

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

Solution 2 - Reactjs

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

Solution 3 - Reactjs

You can save previous path in a componentWillReceiveProps lifecycle method. The logic is very close to the example provided in [troubleshooting section][1] 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. [1]: https://github.com/reactjs/react-router/blob/master/docs/Troubleshooting.md#getting-the-previous-location

Solution 4 - Reactjs

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;
  }
}

Solution 5 - Reactjs

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>
    ):
}

Solution 6 - Reactjs

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;
...

Solution 7 - Reactjs

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

Solution 8 - Reactjs

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/>

Solution 9 - Reactjs

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();

Solution 10 - Reactjs

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

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 };

}

Solution 11 - Reactjs

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

Solution 12 - Reactjs

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

Solution 13 - Reactjs

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');

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionvijaystView Question on Stackoverflow
Solution 1 - ReactjsSam LoganView Answer on Stackoverflow
Solution 2 - ReactjsBryan LeeView Answer on Stackoverflow
Solution 3 - ReactjsAlexandr LazarevView Answer on Stackoverflow
Solution 4 - ReactjsDan AndreassonView Answer on Stackoverflow
Solution 5 - Reactjsshashi vermaView Answer on Stackoverflow
Solution 6 - ReactjsAV PaulView Answer on Stackoverflow
Solution 7 - ReactjslissettdmView Answer on Stackoverflow
Solution 8 - ReactjsGrant HumphriesView Answer on Stackoverflow
Solution 9 - ReactjsMark HetheringtonView Answer on Stackoverflow
Solution 10 - ReactjsJoeView Answer on Stackoverflow
Solution 11 - ReactjsHaratView Answer on Stackoverflow
Solution 12 - ReactjsUranium_x02View Answer on Stackoverflow
Solution 13 - ReactjsaskepottView Answer on Stackoverflow