-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
setQueryData()
should be persisted too, right?
#6310
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
Comments
hmm, good question. Right now, the persister is just a wrapper around the @DamianOsipiuk what are your thoughts here? |
I agree that it would be important if persistence can be somehow implemented with |
one problem I'm seeing is that why does the flow "break" with optimistic updates though? |
Oh and what about if data comes into the cache via hydration 🤔 |
My mistake about the useMutation flow. I didn't have |
For storing the data, if we have a The problem is that As for |
Just some general input as I'm testing out the new
I suppose I'm also abusing the library a bit and it was not really meant for all these use cases though, but I do believe that some of them are valid reasons where
I think this sounds like a fair solution although I also feel like that could cause issues/bugs if someone for example uses |
There are a couple of things that lead me to think having
reads from the cache are synchronous, so
If you have
it doesn't do anything - you can just as well call
For example, you can use Imo, being an "ad-hoc" persister was always part of the plan. Async reads (= running the queryFn) would try to read from the storage first, unless there is data already in the cache. It's like "fallback" data. If we get data into the in-memory cache by other means, we don't need to lookup the fallback. Maybe it's conceptually wrong that the |
Well, the cool thing about current solution is that you can add We could potentially hook to the cache to store it in persister, but I'm kinda worried about adding a listener for every query, if users decide to do it this way. Maybe we should just force users to set up persister globally with We could potentially expose async |
can you elaborate why there's a difference between global and local ones? The global one is just a default value setting - it's functionally equivalent imo to passing
yeah that's an option. We could also just expose the auto-write subscribe hook and then let users decide which way they want ? |
For global persister you call For I beleive that |
right, I see what you mean. It would have to become a hook like
It sets up a subscription for writes and eagerly restores data. We basically only want the writes, and keep the restores lazily. |
In my opinion, the useQuery({
queryKey: ['items'],
queryFn() {
const items = await localDb.getItems()
if (!items) {
const networkItems = await getItemsFromNetwork()
await localDb.saveItems()
return networkItems
}
return items
}
}) In this case, instead of calling |
I would love to be able to For us who work on multiple-page applications, the client cache can get quite big in some cases and loads on every navigation. We are considering just using something like https://github.com/epicweb-dev/cachified , but it would defeat the point of using tanstack query. |
@TorbjornHoltmon cachified seems an amazing idea! |
Any updates on this please ? - With VueJS, I think the only way to persist data between reload/page-refresh is using |
@jai2201 There is a draft MR that splits original implementation to be more granular, allowing you to use returned utilities in user land. Also adding additional utilities for convenience. It needs some tests and docs though. return {
persisterFn,
persistQuery,
retrieveQuery,
persisterGc,
persisterRestoreAll,
} |
It sounds like there is still some work being done here to make
|
@lancej1022 Thankfully you can install the update with
It seems to be working for me with this approach let clientQueryClientSingleton: QueryClient | undefined = undefined
let hasRestoredQueries = false
function newIdbStorage(idbStore: UseStore) {
return {
getItem: async (key: IDBValidKey) => await get(key, idbStore),
setItem: async (key: IDBValidKey, value: any) => {
console.log(`setting ${key}`)
return await set(key, value, idbStore)
},
removeItem: async (key: IDBValidKey) => await del(key, idbStore),
entries: async () => {
return await entries(idbStore)
}
}
}
export const persister = experimental_createQueryPersister({
// @ts-ignore
storage: newIdbStorage(createStore("name", "qc")),
serialize: SuperJSON.serialize,
prefix: "v1",
deserialize: SuperJSON.deserialize,
maxAge: Number.POSITIVE_INFINITY
})
export const createQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
// @ts-ignore
persister: persister.persisterFn,
retry: true,
experimental_prefetchInRender: false,
networkMode: "offlineFirst",
gcTime: 1000 * 60 * 60 * 24, // 24 hours
staleTime: 60 * 1000,
refetchOnReconnect: false,
refetchOnMount: false,
refetchOnWindowFocus: true
}
}
})
export const getQueryClient = () => {
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
const queryClient = (clientQueryClientSingleton ??= createQueryClient())
if (!hasRestoredQueries) {
hasRestoredQueries = true
// @ts-ignore
void persister.persisterRestoreAll(queryClient)
console.log("Restored")
}
return queryClient
} Then in my mutation I do this: queryClient.setQueryData(
["cat"]
data.resultValue
)
const catQuery = queryClient.getQueryCache().find({
queryKey:["cat"],
exact: true
})
persister.persistQuery(catQuery).then(console.log) I'm not totally sure how any of this works so it could all be very wrong |
* Update createPersister.md with info about #6310 * Fix typo * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
A question to Contributors and someone who knows a lot about how Tanstack actually works. Until any official solution goes live, do you think this workaround is viable? import { experimental_createPersister } from '@tanstack/react-query-persist-client';
export const storage = new MMKV();
const clientStorage = {
setItem: (key: string, value: string) => {
storage.set(key, value);
},
getItem: (key: string) => {
const value = storage.getString(key);
return value === undefined ? null : value;
},
removeItem: (key: string) => {
storage.delete(key);
},
};
const persister = experimental_createPersister({
storage: clientStorage,
maxAge: 5 * TimeService.minute,
});
someMethod() {
queryClient.setQueryData(cacheKey, (queryData: unknown | undefined) => {
// some business logic here
// ........
return updatedCacheItem;
});
persistQuery(cacheKey);
}
const persistQuery = (cacheKey: string[]) => {
const state = queryClient.getQueryState(cacheKey);
const queryHash = JSON.stringify(cacheKey);
const storageKey = `tanstack-query-${queryHash}`;
storage.set(
storageKey,
JSON.stringify({
state: state,
queryKey: cacheKey,
queryHash: queryHash,
buster: '',
}),
);
};
So the idea is that after Thanks! |
Describe the bug
I'm using the new
experimental_createPersister
like this:But I just found out that if I call
the in memory cache works (the
team
is added toplayer
) but there is notsetItem()
call for this lastqueryClient.setQueryData()
. If I reload the page the team is not on player anymore (unless I invalidate the player manually).I think this is wrong, because this change should be persisted too.
I'll create a reproduction if you think it might be useful.
How often does this bug happen?
Every time
Platform
Chrome.
Tanstack Query adapter
svelte-query
TanStack Query version
5.4.3
TypeScript version
5
Additional context
I'm using:
The text was updated successfully, but these errors were encountered: