Intercept/handle browser's back button in React-router?
Solution 1
here is how I ended up doing it:
componentDidMount() {
this._isMounted = true;
window.onpopstate = ()=> {
if(this._isMounted) {
const { hash } = location;
if(hash.indexOf('home')>-1 && this.state.value!==0)
this.setState({value: 0})
if(hash.indexOf('users')>-1 && this.state.value!==1)
this.setState({value: 1})
if(hash.indexOf('data')>-1 && this.state.value!==2)
this.setState({value: 2})
}
}
}
thanks everybody for helping lol
Solution 2
This is a bit old question and you've probably already got your answer, but for people like me who needed this, I'm leaving this answer.
Using react-router made the job simple as such:
import { browserHistory } from 'react-router';
componentDidMount() {
super.componentDidMount();
this.onScrollNearBottom(this.scrollToLoad);
this.backListener = browserHistory.listen(location => {
if (location.action === "POP") {
// Do your stuff
}
});
}
componentWillUnmount() {
super.componentWillUnmount();
// Unbind listener
this.backListener();
}
Solution 3
Using hooks you can detect the back and forward buttons
import { useHistory } from 'react-router-dom'
const [ locationKeys, setLocationKeys ] = useState([])
const history = useHistory()
useEffect(() => {
return history.listen(location => {
if (history.action === 'PUSH') {
setLocationKeys([ location.key ])
}
if (history.action === 'POP') {
if (locationKeys[1] === location.key) {
setLocationKeys(([ _, ...keys ]) => keys)
// Handle forward event
} else {
setLocationKeys((keys) => [ location.key, ...keys ])
// Handle back event
}
}
})
}, [ locationKeys, ])
Solution 4
Hooks sample
const {history} = useRouter();
useEffect(() => {
return () => {
// && history.location.pathname === "any specific path")
if (history.action === "POP") {
history.replace(history.location.pathname, /* the new state */);
}
};
}, [history])
I don't use history.listen because it doesn't affect the state
const disposeListener = history.listen(navData => {
if (navData.pathname === "/props") {
navData.state = /* the new state */;
}
});
Solution 5
Most of the answers for this question either use outdated versions of React Router, rely on less-modern Class Components, or are confusing; and none use Typescript, which is a common combination. Here is an answer using Router v5, function components, and Typescript:
// use destructuring to access the history property of the ReactComponentProps type
function MyComponent( { history }: ReactComponentProps) {
// use useEffect to access lifecycle methods, as componentDidMount etc. are not available on function components.
useEffect(() => {
return () => {
if (history.action === "POP") {
// Code here will run when back button fires. Note that it's after the `return` for useEffect's callback; code before the return will fire after the page mounts, code after when it is about to unmount.
}
}
})
}
A fuller example with explanations can be found here.
Related videos on Youtube
high incompetance
Updated on October 14, 2021Comments
-
high incompetance over 2 years
I'm using Material-ui's Tabs, which are controlled and I'm using them for (React-router) Links like this:
<Tab value={0} label="dashboard" containerElement={<Link to="/dashboard/home"/>}/> <Tab value={1} label="users" containerElement={<Link to="/dashboard/users"/>} /> <Tab value={2} label="data" containerElement={<Link to="/dashboard/data"/>} />
If I'm currenlty visting dashboard/data and I click browser's back button I go (for example) to dashboard/users but the highlighted Tab still stays on dashboard/data (value=2)
I can change by setting state, but I don't know how to handle the event when the browser's back button is pressed?
I've found this:
window.onpopstate = this.onBackButtonEvent;
but this is called each time state is changed (not only on back button event)
-
Hyzyr about 2 yearsI know it is late, but call it in useEffect with empty params like :
useEffect(()=>{/* here */}, [])
-
-
loopmode almost 7 yearsdoesn't work for me. using [email protected], location.action is always PUSH, even when clicking the browser back button
-
Jay Shin almost 7 years@loopmode when you handle the back button action, to you use something like
browserHistory.goBack()
?? -
loopmode almost 7 yearsI found that elsewhere, a colleague is meddling with the history manually, something along the lines if (nextLocation.action === 'POP' && getStepIndex(nextLocation.pathname) === 0) { browserHistory.push({pathname:
${getPathForIndex(0)}
}); return false; } so..if POP then PUSH
in order to make the forward button unavailable (which would go forward without a form being submitted) So.. Your answer remains correct - user error on my side :) -
151291 about 5 yearsIt is triggering before click back button, Can you help on this?, we need to trigger a custom popup after click back button.
-
Stoph about 5 yearsAs of [email protected] you can't import
browserHistory
as illustrated in this response. It appears thathistory
is included in theprops
passed to any component that is referenced from a route. Feel free to correct me if that's not quite right. -
Atombit over 4 yearsit is not a react way
-
Eran Or over 4 yearsI think it is the history.action === "POP" and also it happens with refresh too
-
anerco over 4 yearsi would change the dependency array to [history.location, history.action] because it wont catch the location changes
-
mawburn over 4 yearsFor anyone using React Router 4+, then the listen has 2 parameters, location and action.
history.listen((loc, action) => if (action === 'POP') // do stuff)
-
Paulo over 4 yearshow do i prevent the POP from being executed though?
-
Andrew about 4 yearsIsn't
useRouter()
specific to the Next.js framework? -
Andrew about 4 yearsThis illustrates how to create a back button, not intercept the browser's back button, as the OP requested.
-
Nikita Vlasenko almost 4 yearsFor me its just giving an error
TypeError: Cannot read property 'setRouteLeaveHook' of undefined
-
brogrammer almost 4 years@NikitaVlasenko Expanding on the example above,
Foo
needs to be passed to a<Route />
component, or at the very least needs to inherit a route component's props. (E.g., in yourroutes.js
file,<Route component={Foo}>/* ... */</Route>
) -
mheavers almost 4 years
-
Shabbir Essaji over 3 yearsWhat's the _ (underscore) in setLocationKeys(([ _, ...keys ]) => keys)
-
rob-gordon over 3 years@ShabbirEssaji It uses destructing and the spread operator, to return an array with the first element removed. This may help
-
Sam over 3 yearsYou should include the back logic
-
KshitijV97 about 3 yearsWhat does the _ do
-
Peter Moore almost 3 yearsThe "react way" is overly restrictive and byzantine.
-
Josh J almost 3 years@ShabbirEssaji you probably found your answer by now, but an underscore is used a lot by convention when a variable must be assigned but won't be used.
-
Joseph Beuys' Mum over 2 yearsis this still a good way to handle both back and forward or has anything been added to deal with the arguably hacky
locationKeys, setLocationKeys
bit? yet. pls. ty -
Nicolas Keller over 2 yearsSo far yes, this is the best solution we have.
-
Michael Harley over 2 years@KshitijV97 That means "ignore this variable", additionally the convention of
_
can be used like this_firstElement
, and this_firstElement
won't cause any warning/error even if it's not used. -
Emaborsa over 2 yearsIt'a a nice solution, but IMHO not what requested. The request is "How to intercept/handle the back button", but this code triggered even if a new url is set per code.
-
Sriram R about 2 yearsAny hack while using hashRouter? Prompt is triggered after url changes in the browser