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>
);
}
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>
);
}