unstable_shouldReload
shouldRevalidate
, you should use it instead. This hook is no longer called starting in v1.10.0
This function lets apps optimize which routes should be reloaded on some client-side transitions in v1.9.x and lower.
import type { ShouldReloadFunction } from "@remix-run/react";
export const unstable_shouldReload: ShouldReloadFunction =
({
// same params that go to `loader` and `action`
params,
// a possible form submission that caused this to be reloaded
submission,
// the next URL being used to render this page
url,
// the previous URL used to render this page
prevUrl,
}) => false; // or `true`;
During client-side transitions, Remix will optimize reloading of routes that are already rendering, like not reloading layout routes that aren't changing. In other cases, like form submissions or search param changes, Remix doesn't know which routes need to be reloaded, so it reloads them all to be safe. This ensures data mutations from the submission or changes in the search params are reflected across the entire page.
This function lets apps further optimize by returning false
when Remix is about to reload a route. There are three cases when Remix will reload a route, and you have the opportunity to optimize:
url.search
changes (while the url.pathname
is the same)Otherwise, Remix will reload the route, and you have no choice:
url.pathname
changed (including route params)Here are a couple of common use-cases:
It's common for root loaders to return data that never changes, like environment variables to be sent to the client app. In these cases you never need the root loader to be called again. For this case, you can simply return false
.
export const loader = async () => {
return json({
ENV: {
CLOUDINARY_ACCT: process.env.CLOUDINARY_ACCT,
STRIPE_PUBLIC_KEY: process.env.STRIPE_PUBLIC_KEY,
},
});
};
export const unstable_shouldReload = () => false;
With this in place, Remix will no longer make a request to your root loader for any reason, not after form submissions, not after search param changes, etc.
Another common case is when you've got nested routes and a child component has a feature that uses the search params in the URL, like a search page or some tabs with state you want to keep in the search params.
Consider these routes:
└── $projectId.tsx
└── activity.tsx
And let's say the UI looks something like this:
+------------------------------+
| Project: Design Revamp |
+------------------------------+
| Tasks | Collabs | >ACTIVITY |
+------------------------------+
| Search: _____________ |
| |
| - Ryan added an image |
| |
| - Michael commented |
| |
+------------------------------+
The activity.tsx
loader can use the search params to filter the list, so visiting a URL like /projects/design-revamp/activity?search=image
could filter the list of results. Maybe it looks something like this:
export async function loader({
params,
request,
}: LoaderArgs) {
const url = new URL(request.url);
return json(
await exampleDb.activity.findAll({
where: {
projectId: params.projectId,
name: {
contains: url.searchParams.get("search"),
},
},
})
);
}
This is great for the activity route, but Remix doesn't know if the parent loader, $projectId.tsx
also cares about the search params. That's why Remix does the safest thing and reloads all the routes on the page when the search params change.
In this UI, that's wasted bandwidth for the user, your server, and your database because $projectId.tsx
doesn't use the search params. Consider that our loader for $projectId.tsx
looks something like this:
export async function loader({ params }: LoaderArgs) {
return json(await fakedb.findProject(params.projectId));
}
We want this loader to be called only if the project has had an update, so we can make this really simple and just say to reload if there is a non-GET submission:
export function unstable_shouldReload({ submission }) {
return !!submission && submission.method !== "GET";
}
Now if the child route causes the search params to change, this route will no longer be reloaded because there was no submission.
You may want to get more granular and reload only for submissions to this project:
export function unstable_shouldReload({
params,
submission,
}) {
return !!(
submission &&
submission.action === `/projects/${params.projectId}`
);
}
You need to be very careful here, though. That project (or its nested relationships) may be updated by other actions and your app will get out of sync if you don't also consider them.