react-router
react-router copied to clipboard
[Bug]: useNavigate does not work when wrapped in async fn, and the context that wraps the router changes
What version of React Router are you using?
6.21.3
Steps to Reproduce
Based on the provided reproduction, when i click on the "User" link, it goes through Layout and User components. User changes state in context (which wraps the whole router) and it breaks the behaviour of navigate in Layout which is called 1s after rendering (it doesn't do anything).
https://codesandbox.io/p/devbox/6q36k2 (contains below fragment of code)
...
export default function App() {
const [state, setState] = useState(false);
return (
<Ctx.Provider value={{ state, setState }}>
<Router />
</Ctx.Provider>
);
}
const Layout = () => {
const navigate = useNavigate();
const { id } = useParams();
useEffect(() => {
(async () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
// only url changes
navigate(`/users/${id}/other`);
})();
}, []);
return <Outlet />;
};
const User = () => {
const { setState } = useContext(Ctx);
useEffect(() => {
// this is called before `navigate` in `Layout`
setState(true);
}, []);
return <>User</>;
};
...
When i comment setState or assign navigate to useRef everything works correctly.
Expected Behavior
After clicking on the link and waiting 1s, the route /users/${id}/other should render correctly.
Actual Behavior
After clicking on the link and waiting 1s, the url changes to /users/${id}/other but I still see /users/${id}.
We encountered a similar problem today.
Our case might be different from yours, because we only encounter such problem when reloading the page. Turns out there is an check in the library that the navigate cannot be used until the history listener is being wired up.
https://github.com/remix-run/react-router/blob/13ac1b6c5cc2c3f99629bed6ed0ccc8542de0d94/packages/react-router/lib/hooks.tsx#L210-L221
I tried to add the navigate function as a dependency of useEffect, and it seems working for me.
useEffect(() => {
navigate(`/users/${id}/other`);
}, [navigate]);
The documentation didn't tell us to add navigate as a dependency, so I can't assure you that is the right solution.