Search code examples
javascriptreactjsreact-router-dom

how to use createBrowserRouter in react + react router dom?


In my application I am using BrowserRouter component to render my routes or components, but I want to use loader functionality. My current approach I am not able to use that feature.

<BrowserRouter>
  <AppRoutes />
</BrowserRouter>

As suggested by below link I need to use createBrowserRouter function . why loader function not called in react js? https://reactrouter.com/en/main/routers/picking-a-router

current way of routing.

export default function AppRoutes() {
  return (
    <Layout>
      <Routes>
        <Route element={<RequireAuth />}>
          <Route
            path={'/'}
            element={
              <React.Suspense
                fallback={<div>loading</div>}
                children={<Booking />}
              />
            }
          ></Route>
        </Route>
        <Route element={<RequireAuth />}>
          <Route
            path={'/app'}
            element={
              <React.Suspense
                fallback={<div>loading</div>}
                children={<App />}
              />
            }
          ></Route>
        </Route>
      </Routes>
    </Layout>
  );
}

I am trying to refactor my routing using createBrowserRouter. I tried like this:

const router = createBrowserRouter([
  {
    path: '/',
    loader: () => {
      console.log('0000');
      return 'ss';
    },
    element: <RequireAuth />,
  },
  {
    path: '/app',
    loader: () => {
      console.log('0000');
      return 'ss';
    },
    element: <RequireAuth />,
  },
]);

How to render Booking and App components? How I will render children in createBrowserRouter approach any idea?

Here is code sandbox demo: https://stackblitz.com/edit/vitejs-vite-6gg4ab?file=src%2FApp.tsx,src%2Fmain.tsx,src%2Findex.css,src%2Froutes.tsx,src%2Flayout.tsx,src%2FRequireAuth.tsx,src%2Fbrowser-routes.tsx&terminal=dev


Solution

  • The createBrowserRouter function takes an array of RouteObject types

    export interface NonIndexRouteObject {
      caseSensitive?: AgnosticNonIndexRouteObject["caseSensitive"];
      path?: AgnosticNonIndexRouteObject["path"];
      id?: AgnosticNonIndexRouteObject["id"];
      loader?: AgnosticNonIndexRouteObject["loader"];
      action?: AgnosticNonIndexRouteObject["action"];
      hasErrorBoundary?: AgnosticNonIndexRouteObject["hasErrorBoundary"];
      shouldRevalidate?: AgnosticNonIndexRouteObject["shouldRevalidate"];
      handle?: AgnosticNonIndexRouteObject["handle"];
      index?: false;
      children?: RouteObject[];
      element?: React.ReactNode | null;
      hydrateFallbackElement?: React.ReactNode | null;
      errorElement?: React.ReactNode | null;
      Component?: React.ComponentType | null;
      HydrateFallback?: React.ComponentType | null;
      ErrorBoundary?: React.ComponentType | null;
      lazy?: LazyRouteFunction<RouteObject>;
    }
    
    export type RouteObject = IndexRouteObject | NonIndexRouteObject;
    

    The children property takes another array of RouteObject objects, so you can use this recursive structure to build up your routing hierarchy.

    To convert the first code snippet using the BrowserRouter to the new Data Router syntax I suggest the following refactor that:

    1. Converts Layout to a layout route component

      Layout.tsx

      import { Outlet } from 'react-router-dom';
      
      export default function Layout() {
        return (
          <div>
            Layout
            <Outlet />
          </div>
        );
      }
      
    2. Renders a single layout route rendering RequireAuth, this makes for code that is a bit more DRY

    3. Renders Booking and App on nested routes under the auth layout route

      routes.tsx

      import {
        createBrowserRouter,
        Route,
      } from 'react-router-dom';
      
      const router = createBrowserRouter([
        {
          element: <Layout />,
          children: [
            {
              element: <RequireAuth />,
              children: [
                {
                  path: '/',
                  loader: () => {
                    console.log('0000');
                    return 'ss';
                  },
                  element: (
                    <React.Suspense
                      fallback={<div>loading</div>}
                      children={<Booking />}
                    />
                  ),
                },
                {
                  path: '/app',
                  loader: () => {
                    console.log('0000');
                    return 'ss';
                  },
                  element: (
                    <React.Suspense
                      fallback={<div>loading</div>}
                      children={<App />}
                    />
                  ),
                },
              ],
            },
          ],
        },
      ]);
      
      export default router;
      
    4. Update main.tsx to render a RouterProvider and the computed router instead of the BrowserRouter component

      main.tsx

      import React from 'react';
      import ReactDOM from 'react-dom/client';
      import { RouterProvider } from 'react-router-dom';
      import './index.css';
      import router from './routes';
      
      ReactDOM.createRoot(document.getElementById('root')!).render(
        <React.StrictMode>
          <RouterProvider router={router} />
        </React.StrictMode>
      );