The basic gist is that you can keep and maintain your own history stack and check if there are history entries to go back to. If the stack is empty then redirect to the app's home page, otherwise, allow the normal navigation.
Example using a React Context to hold the stack state and provide a customized navigate
function.
import {
createContext,
useCallback,
useContext,
useEffect,
useState
} from "react";
import {
useNavigate as useNavigateBase,
UNSAFE_NavigationContext,
NavigationType
} from "react-router-dom";
const NavigateContext = createContext({
navigate: () => {},
});
const useNavigate = () => useContext(NavigateContext);
const NavigateProvider = ({ children }) => {
const [historyStack, setHistoryStack] = useState([]);
const navigateBase = useNavigateBase();
const { navigator } = useContext(UNSAFE_NavigationContext);
useEffect(() => {
const listener = ({ location, action }) => {
switch (action) {
case NavigationType.Push:
return setHistoryStack((stack) => stack.concat(location));
case NavigationType.Replace:
return setHistoryStack((stack) =>
stack.slice(0, -1).concat(location)
);
case NavigationType.Pop:
return setHistoryStack((stack) => stack.slice(0, -1));
default:
// ignore
}
};
return navigator.listen(listener);
}, [navigator]);
useEffect(() => {
console.log({ historyStack });
}, [historyStack]);
const navigate = useCallback(
(arg, options) => {
if (typeof arg === "number" && arg < 0 && !historyStack.length) {
navigateBase("/", { replace: true });
} else {
navigateBase(arg, options);
}
},
[historyStack, navigateBase]
);
return (
<NavigateContext.Provider value={navigate}>
{children}
</NavigateContext.Provider>
);
};
Example Usage:
const GoBackButton = () => {
const navigate = useNavigate();
return <button onClick={() => navigate(-1)}>go back</button>
}
...
function App() {
return (
<NavigateProvider>
... app code ...
</NavigateProvider>
);
}
