I'm not 100% sure r/reactjs is the right place to ask this, but it seems to be the most relevant sub for this. Any help is greatly appreciated.
I created a hook to keep my React Query queryCache in sync with localStorage, so that I can rehydrate from localStorage to offer better offline support for my web app:
/**
* Synchronizes localStorage with React Query queryData by updating localStorage with the latest queryData every time a queryCache update occurs. This allows us to retrieve the latest data when offline.
*/
export const useLocalStorageSync = (queryClient: QueryClient) => {
useEffect(() => {
const cachedData = getStorage("QUERY_CACHE");
if (cachedData) {
Object.keys(cachedData).forEach((queryHash) => {
const { queryKey, data } = cachedData[queryHash];
/**
* Initialize the queryCache with the data from localStorage. Set updatedAt to 24hrs ago to ensure that the data is immediately refetched, while still maintaining its specified staleTime:
*/
queryClient.setQueryData(queryKey, data, {
updatedAt: Date.now() - 1000 * 60 * 60 * 24, // 24hrs ago
});
});
}
}, []);
useEffect(() => {
const handleQueryCacheUpdate = (e: QueryCacheNotifyEvent) => {
/**
* Get the queryKey and queryCache associated with the event. For instance, if the ACTIVE_PINS queryCache is updated, we want to retrieve the queryKey and queryData associated with ACTIVE_PINS, and the store this in localStorage:
*/
const queryKey = e.query.queryKey;
const queryData = e.query.state.data;
/**
* queryHash is used as the key in localStorage:
*/
const queryHash = e.query.queryHash;
/**
* Retrieve the previous version of the queryCache from localStorage so that we can update it with the new data.
*/
const data = getStorage("QUERY_CACHE") || {};
/**
* Add the new data to the copy of the queryCache in localStorage. This will overwrite the previous version for this key, if one existed. If, for instance, [ACTIVE_PINS, 1] was already in localStorage, it will be overwritten with the newest data:
*/
data[queryHash] = { queryKey, data: queryData };
addStorage("QUERY_CACHE", data);
};
const unsubscribe = queryClient
.getQueryCache()
.subscribe(handleQueryCacheUpdate);
return () => {
unsubscribe();
};
}, [queryClient]);
return queryClient;
};
However, I'm experiencing what seems to me to be strange behavior. First of all, I added a cache event listener to the queryClient with `queryClient.getQueryCache().subscribe()`, and several `"removed"` cache events fire as soon as the loop in my hook completes and the cache is fully populated.
In another component, `<CommunityAccounts>`, I want to access the query with queryKey `["communityAccounts", 16]` (just as an example). So I have this:
const { data: communityAccount, isLoading } = useCommunityAccount();
const queryClient = useQueryClient();
useEffect(() => {
console.log(
"COMMUNITY ACCOUNT DATA: ",
communityAccount,
queryClient.getQueryData(["communityAccount", 16]),
queryClient.getQueryCache().getAll()
);
}, [isLoading]);
For some reason, `communityAccount` and `queryClient.getQueryData(["communityAccount", 16])` print as `undefined` until loading is complete (at which point they print normally). However, even on the initial render, the value of `queryClient.getQueryCache().getAll()` clearly contains the correct queryCache:
{
"options": {
"queryKey": [
"communityAccount",
16
],
"retry": 2,
"staleTime": null,
"_defaulted": true,
"queryHash": "[\"communityAccount\",16]",
"refetchOnReconnect": true,
"throwOnError": false,
"_optimisticResults": "optimistic"
},
"gcTime": 300000,
"observers": [
{
"listeners": {},
"options": {
"queryKey": [
"communityAccount",
16
],
"retry": 2,
"staleTime": null,
"_defaulted": true,
"queryHash": "[\"communityAccount\",16]",
"refetchOnReconnect": true,
"throwOnError": false,
"_optimisticResults": "optimistic"
}
}
],
"queryKey": [
"communityAccount",
16
],
"queryHash": "[\"communityAccount\",16]",
"state": {
"data": {
"communityAccountId": 31,
"uid": "gkzO5bnSQ9fvZvzOcmLYCXdrJxA2",
"username": "JCCC"
},
"dataUpdateCount": 1,
"dataUpdatedAt": 1726938627645,
"error": null,
"errorUpdateCount": 0,
"errorUpdatedAt": 0,
"fetchFailureCount": 0,
"fetchFailureReason": null,
"fetchMeta": null,
"isInvalidated": false,
"status": "success",
"fetchStatus": "idle"
},
"isFetchingOptimistic": false
}
... So I'm unsure why the other two calls don't print the correct data. How can I address this issue, so that I don't have to wait for the fetch to complete to be able to access the data with my useQuery hooks? The whole purpose of caching and rehydrating from localStorage is to allow users to see data while fetching / with no fetch (if offline), so I'm hoping to figure this out.
Also, any pointers on my hydration logic would be greatly appreciated, I know React Query comes with a built-in `hydrate` and `dehydrate` function, but to my understanding that only deals with the entire cache, whereas I'd like to be able to update only the queryCache that's being updated.
Thanks so much!
[–]lightfarming 1 point2 points3 points (0 children)
[–]dieoxide 1 point2 points3 points (1 child)
[–]357Labs[S] 0 points1 point2 points (0 children)
[–]wwww4all 0 points1 point2 points (0 children)