Search code examples
javascriptreactjsreact-routerreact-router-dom

React Router Loader Behavior


I'm currently diving into React-Router, and I've set up my routing structure as follows:

const router = createBrowserRouter([    
  {
    path: "/",
    loader: rootLayoutLoader,
    element: <RootLayout />,
    children: [
      {
        path: "student",
        loader: commonPageLoader,
        element: <Student />,
      },
      {
        path: "teacher",
        loader: commonPageLoader,
        element: <Teacher />,
      },
    ],
  },
]);

In my setup, the rootLayoutLoader function is responsible for calling an API to gather some common information used by both the Teacher and Student components. Meanwhile, the commonPageLoader function is calling an API based on the location path to fetch specific data.

Now, here's the issue I'm encountering: when I'm in the "/teacher" route and click the "Teacher" link, it triggers both the rootLayoutLoader and commonPageLoader functions. How can I prevent this double loading behavior while still ensuring the necessary data is fetched appropriately?


Solution

  • This is how the route loaders just generally work, they will revalidate their data under certain instances to keep the data in sync with routes.

    There are several instances where data is revalidated, keeping your UI in sync with your data automatically:

    • After an action is called from a <Form>.
    • After an action is called from a <fetcher.Form>
    • After an action is called from useSubmit
    • After an action is called from a fetcher.submit
    • When an explicit revalidation is triggered via useRevalidator
    • When the URL params change for an already rendered route
    • When the URL Search params change
    • When navigating to the same URL as the current URL

    You are triggering the last point when clicking the "/teacher" link when already on the "/teacher" path.

    You can implement the shouldRevalidate prop on the route so that it doesn't revalidate when the URL path is the same.

    Basic Example:

    const shouldRevalidate = ({ currentUrl, nextUrl }) => {
      return currentUrl.pathname !== nextUrl.pathname;
    };
    
    const router = createBrowserRouter([    
      {
        path: "/",
        loader: rootLayoutLoader,
        element: <RootLayout />,
        children: [
          {
            path: "student",
            loader: commonPageLoader,
            shouldRevalidate,
            element: <Student />,
          },
          {
            path: "teacher",
            loader: commonPageLoader,
            shouldRevalidate,
            element: <Teacher />,
          },
        ],
      },
    ]);