harryg
harryg

Reputation: 24077

In remix, prevent a nested route fetching the same data as a parent layout

I'm just getting started with a new Remix app, and would like to try and understand a bit more about the data fetching approach for route components.

Let's say I have a route directory structure like so:

app
├── root.jsx
└── routes
    ├── sales.jsx
    └── sales
        └── info.jsx

So I have a sales route, /sales which will render the sales.jsx component. I also have a /sales/info route which is renders the sales/info.jsx component in the Outlet of sales.jsx.

Both sales.jsx and sales/info.jsx need the same data from an external api. So in both of their loader functions I have something like this:

export const loader: LoaderFunction = async ({ request }) => {
  const salesData = await fetchSales(request);

  return { salesData };
};

The loader functions are identical for both.

This allows me the userLoaderData the salesData that's loaded from the api.

However, this data is fetched twice - once from sales.jsx and also from sales/info.jsx, even though the request is identical both times.

This seems very wasteful. My first thought is to introduce some sort of server-side caching layer to intercept the request, but my understanding is these requests happen concurrently anyway so this wouldn't help.

Does Remix have some sort of de-duping functionality or other approach to prevent fetching the same data more than once for a given route when multiple child components need the same data?

Upvotes: 0

Views: 3754

Answers (1)

Kiliman
Kiliman

Reputation: 20312

Loaders run in parallel, so there is no way to prevent duplicate fetches.

However, it is recommended that you only fetch the data that is needed for the specific route/layout.

In your case, if you only need the sales data in the info UI (that is, you're not using it in the loader), you can use the useMatches hook to retreive the data from the parent loader.

export default function SalesInfo() {
  const infoData = useLoaderData() // data from this loader
  const matches = useMatches() // matches includes all data from root to leaf route
  const salesData = matches[matches.length - 2].data // get parent data

  //...
}

Again, this is only available in the UI component, not the loader. If you do need the data in the loader, you will need to re-fetch it. You could use a server-side cache if the query is expensive.

Upvotes: 1

Related Questions