Add revalidatingUrl: URL
argument to ShouldRevalidateFunctionArgs
to better support generic revalidation logic (like opting into pre-single fetch behavior!)
#13486
Replies: 5 comments 3 replies
-
Hey 👋 I've posted the same discussion after migrating from Remix to React Router in that repo. |
Beta Was this translation helpful? Give feedback.
-
I think shared revalidation logic is really important in most large applications, so I agree with the intent of this proposal, and in need of a solution too! In general the ergonomics of using URLs to understand the route structure seems unnatural given the
Also, I wish there was another mechanism to get the current route ID/path without passing the With that said, I think the proposal here makes a lot of sense given the current signature of |
Beta Was this translation helpful? Give feedback.
-
Adding my voice to this thread: I think for applications with lots of dynamic and user-specific data (e.g. apps behind a login) the previous behavior was a better default, since CDN caching was never an option anyways due to the nature of data. While I appreciate the reasons that brought to this change in single fetch, I too think we need two things:
Thanks for considering it! |
Beta Was this translation helpful? Give feedback.
-
Second this... |
Beta Was this translation helpful? Give feedback.
-
In the example from OP - I agree that The old behavior was to revalidate in 4 situations:
1-3 are easily achieved with the current API, it's #4 that you need to be route-aware. Here's a simple implementation you would use for a given route that has a function shouldRevalidate({ currentUrl, nextUrl, currentParams, nextParams, formMethod }) {
let isMutation = formMethod && formMethod !== "GET";
let isPageReload =
currentUrl.pathname + currentUrl.search ===
nextUrl.pathname + nextUrl.search;
let didSearchParamsChange = currentUrl.search !== nextUrl.search;
// Note this needs to account for all dynamic params on this route's path if it has
// multiple, and also should account for any splat params
let didMyParamsChange = currentParams.slug !== nextParams.slug;
return (
isMutation || isPageReload || didSearchParamsChange || didMyParamsChange
);
} To use your factory pattern above, what I would do is pass the params in and do something like this as your factory function function getShouldRevalidate(params?: string[]) {
return ({ currentUrl, nextUrl, currentParams, nextParams, formMethod }) => {
// same logic for isMutation, isPageReload, didSearchParamsChange
let didMyParamsChange = params ?
params.some(p => currentParams[p] !== nextParams[p]) :
false;
return (
isMutation || isPageReload || didSearchParamsChange || didMyParamsChange
);
}
}
// routes/animals.tsx
export const shouldRevalidate = createShouldRevalidate();
// routes/animals.$animal.tsx
export const shouldRevalidate = createShouldRevalidate(['animal']); You could even pretty easily extend that to account for search params that are relevant to the route to avoid unnecessary revalidations when meaningless search params change. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Problem
With the update to single fetch, when navigating child routes, the parent is also revalidated by default.
For the following example navigation,
/animals/dog
to/animals/cat
This is a huge behavior shift which has reasons—that's fine, it's probably a good default for most apps. Our app is very data/server intensive and we intentionally followed this hierarchy to make navigations fast and efficient. The challenge we face, however, is that the old behavior is difficult to generically opt back into.
Consider the
ShouldRevalidateFunctionArgs
:react-router/packages/react-router/lib/router/utils.ts
Lines 168 to 182 in 1518f7d
For a navigation, you can look at
nextUrl
andcurrentUrl
. However, there's no argument indicating what therevalidatingUrl
(or current route, the route where the shouldRevalidate function is defined) is. I cannot create a shared, genericshouldRevalidate
function that all of my routes can import and re-export. To opt into the previous behavior, I have to pass in the route that is callingshouldRevalidate
. Something likeThis simple example is fine, but what happens when you're now nested many routes deep? What happens when there's path variables? Now it can't be a
startsWith
, it has to parse the variables to check for path matching. Also, what happens if I reorganize my routes? I would have to update every shouldRevalidate manually.Proposed Solution
The bottom line is that we need an easy way to identify that a navigation request is a child route navigation and be able to say
shouldRevalidate
is false for this situation.I think the best way to do this would be to add a
revalidatingUrl: URL
argument toShouldRevalidateFunctionArgs
. This would be the url of the current route file.For the following example navigation,
/animals/dog
to/animals/cat
, these are what theShouldRevalidateFunctionArgs
would look like for the two routes.currentUrl
nextUrl
revalidatingUrl
This is a very generic and powerful solution. I think this would provide a lot of value for the future and provides flexibility for all sorts of generic shouldRevalidate use cases. I also hope this would be fairly easy to implement, although I have not looked at any of the code to know for sure.
Other things I considered
defaultShouldRevalidateLegacy
boolean which mimics the pre single-fetch behavior. Yuck.isChildRouteNavigation
boolean... Yuck.shouldRevalidate
is so close to being an elegant solution.Conclusion
I've seen a few people in discord bring up this issue. I'm hoping this official proposal can get us working towards a solution!
Beta Was this translation helpful? Give feedback.
All reactions