history
history copied to clipboard
[feature request] Smarter goBack
React router had in previous versions this feature that you could ask whether goBack
leaves your app or not. I do implement cancel buttons with goBack. But If someone gets there from a link on another site it just throws them back which is usually not desired.
I worked around this by adding fromApp
state prop that would indicate that it is safe to use goBack.
But that feels kinda hackish.
Example
- Open https://twitter.com/ReactTraining/status/1271508493896318977
- Press the back button at the top of the page
- It will redirect you to
/home
as there's nowhere to go back in the History API (instead of naively redirecting you to some other website you had in the tab before opening the link above)
Suggested solution
It would be nice if goBack
had an optional fallback
argument. So suppose we called history.goBack('/home')
. In case going back would leave the website, it would call replace
with the given fallback
instead.
Workaround
I've been using a wrapper that covers my particular use case. The fallback
argument only accepts a path string and it's not even optional, but you'll get the idea:
import {
BrowserHistoryBuildOptions,
History as OriginalHistory,
createBrowserHistory,
} from 'history'
const initialLocationKey = '__INITIAL_LOCATION__'
export interface History extends Omit<OriginalHistory, 'goBack'> {
goBack: (fallback: string) => void
}
export const createHistory = (options?: BrowserHistoryBuildOptions): History => {
const history = createBrowserHistory(options) as History
// Put a flag on the state of the initial location. We'll check for this flag
// whenever we need to know whether the current location is the initial one in
// the browser history.
history.replace({
...history.location,
state: {
[initialLocationKey]: true,
},
})
history.goBack = (fallbackPath: string) => {
const state = history.location.state as any
// Are we in the initial location? In case so, 'goBack' would originally
// make us leave the website. Instead, let's use 'replace' to go to the
// fallback path.
if (state && state[initialLocationKey]) {
history.replace(fallbackPath, state)
return
}
window.history.go(-1)
}
return history
}
I just found out this lib keeps an idx
property in the window.history.state
which will be 0
when the current route is the first route since the web app has been mounted. So I'm able to use the following custom hook with React Router v6:
import { To } from 'history'
import { useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
export const useGoBack = () => {
const navigate = useNavigate()
const goBack = useCallback(
(fallback: To) => {
// Going back would leave the website?
if (window.history.state.idx === 0) {
navigate(fallback, { replace: true })
return
}
navigate(-1)
},
[navigate],
)
return goBack
}
Usage:
const goBack = useGoBack()
const handleClose = useCallback(() => {
goBack('/home')
})
@gustavopch I'm not seeing idx
on window.history.state
, not sure that's the case with the latest version