-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RTK Query gets stuck in "pending" state when following the persistence example from the website #4715
Comments
Yeah, persistence is outside the scope of RTK, so better documentation is the best answer here. |
Do you have a preference on which of the solution you'd like to see in the documentation? I'll add a bit of example code and pros and cons to get started. It will be a longer post again, sorry in advance 😅. Using transforms
The first two cons could be countered by either:
The last con could be countered by a separate package too, provided the package has a trusted and active maintainer (which is why I suggested releasing that under the reduxjs namespace, but I'm sure Ambassify would be willing to releasing it too if desired.) Note: While it won't hurt to add it, it is no longer required to use Some code illustrating what we did at Ambassify (for persisting the api reducer.) import { pickBy } from 'lodash';
import { createTransform } from 'redux-persist';
import storage from 'redux-persist/lib/storage'
import { QueryStatus } from '@reduxjs/toolkit/query';
/**
* We only want to persist fulfilled queries. Caching anything else doesn't
* make much sense and can even cause bugs: https://github.com/reduxjs/redux-toolkit/issues/4715
*/
const whitelistFulfilledQueries = createTransform(
(inboundState: any) => pickBy(inboundState, { status: QueryStatus.fulfilled }),
(outboundState: any) => pickBy(outboundState, { status: QueryStatus.fulfilled }),
{ whitelist: [ 'queries' ] }
);
/**
* We don't want to persist mutations, subscriptions, config, ... The cache is
* only used when the application loads so none of those are useful at that point.
*/
const removeNonQueries = createTransform(
() => ({}),
() => ({}),
{ blacklist: [ 'queries' ] }
);
const persistConfig = {
key: 'root',
storage,
transforms: [
whitelistFulfilledQueries,
removeNonQueries,
]
} Using a custom state reconciler
import storage from 'redux-persist/lib/storage'
function rtkqStateReconciler(inboundState, originalState, reducedState) {
return reducedState;
}
const persistConfig = {
key: 'root',
storage,
stateReconciler: rtkqStateReconciler,
} |
Problem description
When you set up your store to persist your api reducer as outlined on the website, there's a chance your persisted queries get stuck in "pending" state if the app gets interrupted while fetching a query (e.g. the window is reloaded.) This only happens when a few conditions are met:
autoMergeLevel1
as state reconciler for redux persistextractRehydrationInfo
in your api slice as documented on the websiteWhen both those condition are met, the following will happen upon rehydration:
{ queries: {}, mutations: {}, config: {...} }
REHYDRATE
action is triggeredextractRehydrationInfo
runs and returns theREHYDRATE
payload to RTKQfulfilled
orrejected
on thequeries
object. In our case that's none of them (condition 2 above.) This happens here:redux-toolkit/packages/toolkit/src/query/core/buildSlice.ts
Lines 390 to 399 in 24286f1
REHYDRATE
, redux-persist will useautoMergeLevel1
to do a shallow compare between the state before and after REHYDRATE. It will not detect any changes to thequeries
subkey of the state at this point and resort to retaining what it loaded from storage (i.e. the pending queries.) This happens here: https://github.com/rt2zz/redux-persist/blob/d8b01a085e3679db43503a3858e8d4759d6f22fa/src/stateReconciler/autoMergeLevel1.ts#L24Example
I've set up a minimal example that shows the issue here: https://codesandbox.io/p/sandbox/qpv4m9
Inside the
getPokemonByName
endpoint, it does awindow.reload()
to simulate the user reloading the page. So if the code worked, the example would be in an infinite reload loop. Instead it will rehydrate the pending query from localStorage after the first reload and get stuck there.Towards a solution
I don't think there's a bug in redux-persists, nor in RTK Query. Both work as advertised by themselves.
The problem is that the example code on the RTK website on how to make them work together doesn't work in all scenarios. I can think of two ways to resolve this:
reducedState
(i.e. just use whatever is returned by the api reducer duringREHYDRATE
.)I'm happy to help out on either one but I'd be interested in hearing the team's thoughts on this first.
redux-persist
-specific code in the library?redux-persist-transform-rtk-query
like the others they list on their README by independent developers.)The text was updated successfully, but these errors were encountered: