Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Router v6.0.0-alpha.5 history prop removal - navigating outside of react context

According to the latest v6.0.0-alpha.5 release of react router, the history prop has been removed: https://github.com/ReactTraining/react-router/releases/tag/v6.0.0-alpha.5

Removed the <Router history> prop and moved responsibility for setting up/tearing down the listener (history.listen) into the wrapper components (<BrowserRouter>, <HashRouter>, etc.). <Router> is now a controlled component that just sets up context for the rest of the app.

Navigating within the react context is simple with the useNavigate hook.

But, how does the removal of the history prop affect programmatically navigating outside of the react context? For example, how would we keep our history in sync in order to navigate from inside redux, or an axios/http interceptor, etc., when we no longer can pass the history object? Current V5 implementation: https://reacttraining.com/react-router/web/api/Router

Or, from v6 onwards is the goal to rely on navigating from within react components only?

like image 804
T-G Avatar asked Nov 07 '22 07:11

T-G


1 Answers

Thanks for the question, we know this is going to come up a lot. This is a common question we've gotten for years. Please be patient with us as we begin documenting all of these kinds of things, there's a lot to do!

Short answer: Typically people use thunks for async work that leads to wanting to navigate somewhere else (after a login, after a record is created, etc.). When your thunk is successful, change the state to something like "success" or "redirect" and then useEffect + navigate:

export function AuthForm() {
  const auth = useAppSelector(selectAuth);
  const dispatch = useAppDispatch();
  const navigate = useNavigate();

  useEffect(() => {
    if (auth.status === "success") {
      navigate("/dashboard", { replace: true });
    }
  }, [auth.status, navigate]);

  return (
    <div>
      <button
        disabled={auth.status === "loading"}
        onClick={() => dispatch(login())}
      >
        {auth.status === "idle"
          ? "Sign in"
          : auth.status === "loading"
          ? "Signing in..."
          : null}
      </button>
    </div>
  );
}

A bit more explanation:

For example, how would we keep our history in sync in order to navigate from inside redux

We've always considered this bad practice and reluctantly provided the history objects as first-class API to stop having philosophical conversations about app state and the URL 😅.

But today things are a bit different. The conversation isn't just philosophical anymore but has some concrete bugs when mixed with React's recent async rendering, streaming, and suspense features. To protect react router apps from synchronization bugs with the URL (that developers can't do anything about), v6 no longer exposes the history object.

Hopefully this explanation will help:

Changing the URL is a side-effect, not state. Thunks are used to perform side-effects that eventually figure out some state for the state container but aren't used for the side-effect in and of itself (at least that's my understanding).

For example, you may want to change the focus on the page after your redux state changes. You probably wouldn't try to synchronize and control the document's focus at all times through redux actions and state. Scroll position is the same. Ultimately the user is in control of these things: they can hit the tab key, click on something, or scroll around. Your app doesn't try to own or synchronize that state, it just changes it from time to time in response to actions and state that you do control.

The URL is the same. Users can type whatever they want into the address bar, click back, forward, or even click and hold the back button to go 3 entries back! It's the same kind of state as focus and scroll positions: owned by the user. The container can't ever truly own the URL state because it can't control the actions surrounding it. Mix in React's new and upcoming features and you're gonna lose that game.

In a nutshell: Change redux state > useEffect in the UI > navigate. Hope that helps!

like image 71
Ryan Florence Avatar answered Nov 14 '22 21:11

Ryan Florence