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?
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.
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
browserHistory.push()
–
Paratrooper 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 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 props.location.state.prevPath
–
Bertrand 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 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
from
but thanks to you I found it. const history = useHistory(); console.log(history.location.state.from);
–
Irretentive history.location.from
not history.location.state.from
@Irretentive –
Luce 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;
}
}
import { LOCATION_CHANGE } from 'react-router-redux';
–
Cesar connected-react-router
you can use import { LOCATION_CHANGE } from 'connected-react-router';
–
Mcgean @@router/LOCATION_CHANGE
on console –
Loats 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.
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>
):
}
history.goBack()
, you will encounter the same page twice, which is not you usually want. –
Lugo 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();
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;
...
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;
...
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
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/>
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()
const { url, prevUrl } = importedFunc();
Be carefull in SSR : the window object is undefined. –
Tachistoscope 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
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
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');
history.location.pathname
is the current path. –
Jolley 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);
}
© 2022 - 2024 — McMap. All rights reserved.
this.props.history.location.pathname
. It should work. – Hallerson