reactjsreact-routerreact-router-domreact-suspensereact-lazy-load

React router with lazy and Suspense always falls back on wildcard route on page refresh


I implemented lazy loading routes with lazy and suspense in order to reduce the main bundle size.

However, I run into the issue that, when refreshing the page, the app always end up in the wildcard route instead of reloading at the current location url.

If I remove the wildcard, the issue seems to be fixed, but then I miss the behavior of being redirected on wrong url input. If I remove the lazy/suspense combo and load the routes on the main bundle, the issue also is fixed, but then I obviously lose the benefits of lazy loading.

Why is that ? What could be done to allow the refresh to end up on the previous route ?

// Lazy imports
const Home = lazy(() => import("./home/Home"));
const RecordsModule = lazy(() => import("./records/Records"));

function App() {
  // ...some logic
  // No use of lazy loading here
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <Redirect to={isLoggedIn ? "/main" : "/auth"} />
        </Route>
        <Route path="/auth">
          <Auth />
        </Route>
        <Route path="/main">
          {!isLoggedIn && <Redirect to="/auth" />}
          <Main />
        </Route>
        <Route path="*">
          <Redirect to="/" />
        </Route>
      </Switch>
      <Toaster />
    </BrowserRouter>
  );
}

function Main() {
  // ...some other logic
  /*
    Here the Home and RecordsModule components are lazy loaded.
    If the user refreshes the page while being on /main/records,
    the resulting refresh will bring him on /main/home.
    
    If I put a console.log in the wildcard route,
    it appears to be the route used after the refresh.
    
    The issue is not present if not using lazy loading or removing the wildcard route.
  */
  return (
    <div className="main">
      <Switch>
        <ErrorBoundary>
          <Suspense
            fallback={
              <FullPageContainer>
                <Spinner />
              </FullPageContainer>
            }
          >
            {/* This will redirect to /main/home if coming from /main  */}
            <Route exact path={match.path}>
              <Redirect to={`${match.path}/home`} />
            </Route>
            {/* /main/home */}
            <Route path={`${match.path}/home`}>
              <Home />
            </Route>
            {/* /main/records */}
            <Route path={`${match.path}/records`}>
              <RecordsModule />
            </Route>
            {/* Redirects to /main/home if none of the above is matched */}
            <Route path="*">
              <Redirect to={`${match.path}/home`} />
            </Route>
          </Suspense>
        </ErrorBoundary>
      </Switch>
      <Menu />
    </div>
  );
}

Solution

  • Solved by migrating to React router v6.

    npm i react-router@latest
    

    Migration guide from v5