T
TanStack3y ago
rival-black

Persist wiped every other refresh. Meta not being loaded/persisted?

Hello, I'm noticing a weird behavior where: 1. I open the page, a network request is made, the response is cached 2. I refresh the page, the cached data is loaded. However the persist cache gets wiped because meta is undefined 3. I refresh the page, a network request is made even though it shouldn't Relevant code:
const persistOptions: Omit<PersistQueryClientOptions, 'queryClient'> = {
persister: asyncStoragePersister,
dehydrateOptions: {
shouldDehydrateQuery: (query) => {
const defaultResponse = defaultShouldDehydrateQuery(query);
const isStorable = (query.meta?.storable as boolean) ?? false;

console.log(query.queryKey, defaultResponse, isStorable, query.meta);

return defaultResponse && isStorable;
},
},
};

<PersistQueryClientProvider
client={queryClient}
persistOptions={persistOptions}
onSuccess={() => {
setLoading(false);
}}
>
const persistOptions: Omit<PersistQueryClientOptions, 'queryClient'> = {
persister: asyncStoragePersister,
dehydrateOptions: {
shouldDehydrateQuery: (query) => {
const defaultResponse = defaultShouldDehydrateQuery(query);
const isStorable = (query.meta?.storable as boolean) ?? false;

console.log(query.queryKey, defaultResponse, isStorable, query.meta);

return defaultResponse && isStorable;
},
},
};

<PersistQueryClientProvider
client={queryClient}
persistOptions={persistOptions}
onSuccess={() => {
setLoading(false);
}}
>
const query = useQuery({
queryKey,
async queryFn() {
return getFn();
},
suspense: true,
meta: {
storable: true,
},
enabled: !isDemoMode,
networkMode: 'offlineFirst',
staleTime: ms('1 day'),
cacheTime: ms('30 days'),
});
const query = useQuery({
queryKey,
async queryFn() {
return getFn();
},
suspense: true,
meta: {
storable: true,
},
enabled: !isDemoMode,
networkMode: 'offlineFirst',
staleTime: ms('1 day'),
cacheTime: ms('30 days'),
});
8 Replies
rival-black
rival-blackOP3y ago
Logs from first refresh:
(4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true true {storable: true}
(4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true true {storable: true}
Logs from second refresh
(4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
(4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true false undefined
Logs from third refresh
(4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true true {storable: true}
(4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] false true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true true {storable: true}
ReactQuery.tsx:49 (4) ['store', 'dashboards', '43f4eb21-098b-487a-88fc-11005f3d6ce9', 'server'] true true {storable: true}
Looking at the local storage, the first refresh has the item in the queries array. The second refresh, it's an empty array 😦 Then third refresh it gets added again after the network request. Basically seems like on the second refresh the store is being reset
manual-pink
manual-pink3y ago
Hm, I think the problem is that we only persist the QueryState, not the meta alongside it. So when you restore, meta is undefined everywhere. Then, you fetch one query, and that likely removes all other queries, because they don't have meta set. One way to solve this on your end would be to default to storable: true and then opt-out via storable: false somewhere along the way another option that would require a change on our end is to store meta when we persist. I'm not sure if we can just do that, because meta can contain non-serializable things. But actually, so can queries themselves 😅 So, I think we could just do it, but I need to think a bit about it. Can you file an issue please, best with a reproduction on codesandbox
rival-black
rival-blackOP3y ago
Ahhh that explains it, I actually used to default it to true and never noticed this issue. I wanted to be more selective of what I save hence the flip to default to false. Yeah Ill go ahead and file a ticket + repro
manual-pink
manual-pink3y ago
Thank you 🙏 FYI a fix might go to v5 if we go ahead like that because it could be seen as breaking
rival-black
rival-blackOP3y ago
Uh oh I cant reproduce it: https://codesandbox.io/s/react-typescript-forked-j729lq?file=/src/Query.tsx 😅 Might be something wrong with my code Hmm shouldDehydrateQuery isnt being called
manual-pink
manual-pink3y ago
I think in your example, it works because the Query is rendered immediately. So for that query, meta is set as the useQuery "sets it" The problem should appear if you have stored multiple queries from multiple screens, say A, B and C. If you open the page and render query A, it will likely remove B and C because they don't have "meta" yet
rival-black
rival-blackOP3y ago
Got it to reproduce, thanks for the pointer!
rival-black
rival-blackOP3y ago
GitHub
Persist meta in queries · Issue #5647 · TanStack/query
Describe the bug Initially discussed here: https://discord.com/channels/719702312431386674/1121385264569397259 The meta is not persisted which can lead to interesting/unexpected (developer created)...

Did you find this page helpful?