javascriptreactjsreact-router-dom

Sidebar/Navbar component re-rendering


I am developing a React application using React-Router-DOM v6.27.

The application has a logon and register route as well as a default route that renders an Application component. The Application component includes a NavBar and a TopBar as well as an Outlet for all of the sub-routes defined beneath Application.

When navigating between various paths within the application which are all sub-routes of the default path, I am noticing the NavBar is re-rendering on each navigation, causing a loss of state, which in my case is the "collapsed" state of the NavBar. How can I restructure my routes such that the NavBar is not re-rendering on each navigation causing it to lose state?

function App() {
  const [theme, colorMode] = useMode();
  const { QueryFeedback, queryClient } = useFeedbackQuery();

  const Application = () => {
    return (
      <AuthenticatedView>
        <div className="app">
          <NavBar />
          <div className="content">
            <Topbar />
            <main className="view">
              <Outlet />
            </main>
          </div>
          <QueryFeedback />
        </div>
      </AuthenticatedView>
    );
  }

  return (
    <QueryClientProvider client={queryClient}>
      <ColorModeContext.Provider value={colorMode}>
        <ThemeProvider theme={theme}>
          <CssBaseline />
          <Routes>
            <Route path="/" element={<Application />}>
              <Route index element={<Dashboard />} />
              <Route path="courses" element={<Courses />} />

              <Route path="course/:id" element={<CourseView />}>
                <Route path="" element={<CourseDetail />} />
                <Route path=":moduleid" element={<ModuleDetail />} />
              </Route>

              <Route path="announcements" element={<Announcements />} />
              <Route path="calendar" element={<Calendar />} />

              <Route path="*" element={<NotFound />} />
            </Route>
            <Route path="login" element={<Login />} />
            <Route path="register" element={<Register />} />
          </Routes>
        </ThemeProvider>
      </ColorModeContext.Provider>
    </QueryClientProvider>
  );
}

Solution

  • You have declared the Application component within another React component, a common React anti-pattern. This unnecessarily re-declares the Application component and each time App renders it unmounts the "old instance" and mounts the "new instance"`. When the old components unmount any state they had is garbage collected, the new components start with initial state values.

    Move the Application declaration outside the ReactTree so it's a stable component reference.

    Example:

    const Application = () => {
      const { QueryFeedback } = useFeedbackQuery();
    
      return (
        <AuthenticatedView>
          <div className="app">
            <NavBar />
            <div className="content">
              <Topbar />
              <main className="view">
                <Outlet />
              </main>
            </div>
            <QueryFeedback />
          </div>
        </AuthenticatedView>
      );
    }
    
    function App() {
      const [theme, colorMode] = useMode();
      const { queryClient } = useFeedbackQuery();
    
      return (
        <QueryClientProvider client={queryClient}>
          <ColorModeContext.Provider value={colorMode}>
            <ThemeProvider theme={theme}>
              <CssBaseline />
              <Routes>
                <Route path="/" element={<Application />}>
                  <Route index element={<Dashboard />} />
                  <Route path="courses" element={<Courses />} />
    
                  <Route path="course/:id" element={<CourseView />}>
                    <Route path="" element={<CourseDetail />} />
                    <Route path=":moduleid" element={<ModuleDetail />} />
                  </Route>
    
                  <Route path="announcements" element={<Announcements />} />
                  <Route path="calendar" element={<Calendar />} />
    
                  <Route path="*" element={<NotFound />} />
                </Route>
                <Route path="login" element={<Login />} />
                <Route path="register" element={<Register />} />
              </Routes>
            </ThemeProvider>
          </ColorModeContext.Provider>
        </QueryClientProvider>
      );
    }