react-router icon indicating copy to clipboard operation
react-router copied to clipboard

[Bug]: useNavigate does not work when wrapped in async fn, and the context that wraps the router changes

Open nonameprogram opened this issue 1 year ago • 1 comments

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}.

nonameprogram avatar Jan 31 '24 20:01 nonameprogram

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.

tmak-ec avatar Feb 22 '24 21:02 tmak-ec