Feedback wanted: React query or RTK Query with loaders using the new data strategy #11527
Replies: 7 comments 8 replies
-
Good to see that React Router is willing to provide a way to completely customize data fetching. Maybe we can propose a few "standard" strategies, like the one you propose with the unloaders, or some single-fetch strategy (which seems to be the real reason why they're introducing this feature), but since it's still an unstable feature, I think it is reasonable to keep it as it is. The only small hack that I do not like is the hot reload thing, but that is such an edge case that I do not have any better idea about how to solve it. I'd prefer having this handled directly by the library instead of relying on each developer, especially because it's a debug-time-only issue. |
Beta Was this translation helpful? Give feedback.
-
@FlorentHoareau thanks for the feedback.
yeah I like the API. It really gives a lot of flexibility to the users.
yeah, hopefully in the future things like this will come out of the box. But until then, I think this will help managing subscriptions for things like RTK Query. So I will start using this until I find something better. |
Beta Was this translation helpful? Give feedback.
-
Curious if anything has changed with React Router 7? |
Beta Was this translation helpful? Give feedback.
-
It doesn't seem to unsubscribe to queries when user navigates to a route that doesn't have a loader function. react-router doesn't execute the dataStrategy function so there is no way to stop subscribing to the data. Do you know of a way to unsubscribe when user leaves route? I haven't been able to find such an API to do this for. In the meantime, I've put a dummy loader function on each route that doesn't have a loader function. |
Beta Was this translation helpful? Give feedback.
-
I noticed since import type {
DataStrategyMatch,
DataStrategyFunction,
DataStrategyResult,
} from "react-router-dom";
type OnUnloadArg = () => void;
export type HandlerCtx = { onUnload: (fn: OnUnloadArg) => void };
export function createDataStrategy() {
const unloaderEventTarget = new EventTarget();
let previousMatches: string[] = [];
// fire unload event for each unloading route
const dispatchUnloadEvents = (matches: DataStrategyMatch[]) => {
previousMatches.forEach((id) => {
const match = matches.find((m) => m.route.id === id);
// Dispatch unload event IF
// - if the entry is not matched anymore
// - if the entry is still matched but shouldLoad is true, because the new loader will add this again
if (!match || match.shouldLoad) {
unloaderEventTarget.dispatchEvent(new CustomEvent(`unload-${id}`));
}
});
// store matches for next run
previousMatches = matches.map((match) => match.route.id);
};
// add second param to loaders which is a callback which get's called when the route is not a match anymore
const dataStrategy: DataStrategyFunction = async ({ matches }) => {
dispatchUnloadEvents(matches);
// Run loaders in parallel with the `context` value
const matchesToLoad = matches.filter((m) => m.shouldLoad);
const results: DataStrategyResult[] = await Promise.all(
matchesToLoad.map((match: DataStrategyMatch) =>
match.resolve(async (handler) => {
// add unload handler and pass it to loader as argument
const onUnload = (fn: () => void) => {
unloaderEventTarget.addEventListener(
`unload-${match.route.id}`,
fn,
{ once: true },
);
};
// Whatever you pass to `handler` will be passed as the 2nd parameter
// to your loader/action
return handler({ onUnload });
}),
),
);
const dataStrategyResults: Array<[string, DataStrategyResult]> =
results.map((result, i) => [matchesToLoad[i]?.route.id ?? "", result]);
return Object.fromEntries(dataStrategyResults);
};
return { dataStrategy, dispatchUnloadEvents };
} |
Beta Was this translation helpful? Give feedback.
-
A bit late here… I probably don’t have the full context but this seems a bit overly complex to me. |
Beta Was this translation helpful? Give feedback.
-
@ChristophP How will Automated Re-fetching work in this case? What I mean is that if we move RTK queries to the loader, then redux must somehow be able to call the loader (probably via react-router action). |
Beta Was this translation helpful? Give feedback.
-
Subscription management with loaders
Data loading and caching libraries such as RTK Query use a subscription based model to determine how long entries in the cache have to be kept around.
This typically works with hooks and a lot of discussions out there suggest using a component, but I felt that the whole point of the data APIs (since react router 6.4) is to get data loading matters out of the components tree.
Getting the loading for libs like RTK Query and react query out of the component tree properly thus far has been tough to do with react-router. Do do this with RTK Query one would have to subscribe to "start listening for data" and unsubscribe to "stop listening" when data is no longer needed.
React router offered no way to my knowledge to figure out when "a route no longer matches".
The following is my attempt to provide an API for this, using the the
unstable_dataStrategy
API shipped in react-router version 6.23.Feedback welcome
Since I am looking to use this I would love to hear some opinions about how well you thin this would work.
Do you see any problems with it?
Do you have the same need to manage subscriptions for a data loading lib that was previously hard to achieve?
Is this solution in line with intented usage of the data strategy API?
Do you see an easier way to do this?
General idea
onUnload(handler)
function to the loader. It takes a function as an argument and when called that argument function will be registered to run when the route no longer matches.Implementation
Usage
Loader Example for RTK Query
Related issues and discussions
Beta Was this translation helpful? Give feedback.
All reactions