I am starting a new React project and want to use the latest version of React Router. The docs recommend using createBrowserRouter for all web projects. They don't say why it is better than using BrowserRouterother than it enables some data APIs. Is this the only advantage.
Additionally there is a note in the docs saying
Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the Remixing React Router blog post and the When to Fetch conference talk.
I'm not exactly sure what they mean by outside the React tree isn't everything in my project in the React tree.
Currently I am implementing my router like this:
Main.tsx
ReactDOM.createRoot(document.getElementById('root')!).render(
<GenReMsal>
<React.StrictMode>
<App />
</React.StrictMode>
</GenReMsal>
)
App.js
function App({ msalContext }: AppProps) {
return (
<>
<AuthenticatedTemplate>
<Router currentUser={msalContext.accounts[0]} />
</AuthenticatedTemplate>
</>
)
}
Router.js
function Router({ currentUser }: Props) {
const userInfo = { name: currentUser.name }
const router = createBrowserRouter([
{
path: '/:appTypeId?',
element: <AppLayout currentUser={userInfo} />,
loader: defaulNewAPIs,
children: [
{
index: true,
element: <ReferralDetails status="new" />,
},
],
},
])
return <RouterProvider router={router} />
}
This doesn't seem to be outside the React tree but I am not exactly sure what would.
Additionally there is a note in the docs saying
Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the Remixing React Router blog post and the When to Fetch conference talk.
I'm not exactly sure what they mean by outside the React tree isn't everything in my project in the React tree.
What this basically means is that is it recommended to create the router outside any React component such that it is a stable reference.
Example:
const router = createBrowserRouter([
...routes...
]);
function App() {
return <RouterProvider router={router} />;
}
In your implementation the router is created inside the Router component.
function Router({ currentUser }: Props) {
const userInfo = { name: currentUser.name }
const router = createBrowserRouter([
{
path: '/:appTypeId?',
element: <AppLayout currentUser={userInfo} />,
loader: defaulNewAPIs,
children: [
{
index: true,
element: <ReferralDetails status="new" />,
},
],
},
]);
return <RouterProvider router={router} />;
}
Router is rendered by App.
function App({ msalContext }: AppProps) {
return (
<>
<AuthenticatedTemplate>
<Router currentUser={msalContext.accounts[0]} />
</AuthenticatedTemplate>
</>
);
}
Any time a component higher up in the ReactTree than Router, e.g. AuthenticatedTemplate or App, rerenders, their entire sub-ReactTree is rerendered. What this means in Router now is that the router will be redeclared and a new reference and this will necessarily remount any of the routed components that may be rendered.
Since your router appears to have a dependency on the currentUser prop a solution here could be to memoize the router value.
function Router({ currentUser }: Props) {
const router = React.useMemo(() => {
return createBrowserRouter([
{
path: '/:appTypeId?',
element: <AppLayout currentUser={{ name: currentUser.name }} />,
loader: defaulNewAPIs,
children: [
{
index: true,
element: <ReferralDetails status="new" />,
},
],
},
]);
}, [currentUser]);
return <RouterProvider router={router} />;
}
A better solution however would be to provide the currentUser or the entire msalContext value via a React context to your app and decouple if from your routing logic.
Example:
const MsalContext = React.createContext( .... );
const useMsalContext = () => React.useContext();
const MsalContextProvider = ({ children, msalContext }) => {
return (
<MsalContext.Provider value={msalContext}>
{children}
</MsalContext.Provider>
);
};
function App({ msalContext }: AppProps) {
return (
<MsalContextProvider msalContext={msalContext}>
<AuthenticatedTemplate>
<Router />
</AuthenticatedTemplate>
</MsalContextProvider>
)
}
const router = createBrowserRouter([
{
path: '/:appTypeId?',
element: <AppLayout />,
loader: defaulNewAPIs,
children: [
{
index: true,
element: <ReferralDetails status="new" />,
},
],
},
]);
function Router() {
return <RouterProvider router={router} />;
}
const AppLayout = () => {
const {
accounts: [
{ name } // msalContext.accounts[0].name -> name
]
} = useMsalContext();
...
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With