T
TanStack2mo ago
rival-black

queryOptions not persisted during the hydration/dehydration process

Is there any reason why the below hydration method (and also the dehydration method) does not persist options like gcTime and staleTime? Seems like it only does it for queryKey, queryHash, and meta. This causes some issues in TS Start when defining queries with custom options on the server as those completely get lost on the client. https://codesandbox.io/p/github/andyabih/tmp-start-query-options/main You can check this here. If you go to the test query tab, do a refresh and start incrementing, you'll notice options reset to the default (30,000 gc for example). Happy to dive into this deeper with either an issue or a PR, just want to understand if this is expected behavior or an actual issue. Thanks!
function hydrate(client, dehydratedState, options) {
if (typeof dehydratedState !== "object" || dehydratedState === null) {
return;
}
const mutationCache = client.getMutationCache();
const queryCache = client.getQueryCache();
const deserializeData = options?.defaultOptions?.deserializeData ?? client.getDefaultOptions().hydrate?.deserializeData ?? defaultTransformerFn;
const mutations = dehydratedState.mutations || [];
const queries = dehydratedState.queries || [];
mutations.forEach(({ state, ...mutationOptions }) => {
mutationCache.build(
client,
{
...client.getDefaultOptions().hydrate?.mutations,
...options?.defaultOptions?.mutations,
...mutationOptions
},
state
);
});
queries.forEach(
({ queryKey, state, queryHash, meta, promise, dehydratedAt }) => {
const syncData = promise ? tryResolveSync(promise) : void 0;
const rawData = state.data === void 0 ? syncData?.data : state.data;
const data = rawData === void 0 ? rawData : deserializeData(rawData);
let query = queryCache.get(queryHash);
const existingQueryIsPending = query?.state.status === "pending";
const existingQueryIsFetching = query?.state.fetchStatus === "fetching";

if (query) {
const hasNewerSyncData = syncData && // We only need this undefined check to handle older dehydration
// payloads that might not have dehydratedAt
dehydratedAt !== void 0 && dehydratedAt > query.state.dataUpdatedAt;
if (state.dataUpdatedAt > query.state.dataUpdatedAt || hasNewerSyncData) {
const { fetchStatus: _ignored, ...serializedState } = state;
query.setState({
...serializedState,
data
});
}
} else {
query = queryCache.build(
client,
{
...client.getDefaultOptions().hydrate?.queries,
...options?.defaultOptions?.queries,
queryKey,
queryHash,
meta
},
// Reset fetch status to idle to avoid
// query being stuck in fetching state upon hydration
{
...state,
data,
fetchStatus: "idle",
status: data !== void 0 ? "success" : state.status
}
);
}
if (promise && !existingQueryIsPending && !existingQueryIsFetching && // Only hydrate if dehydration is newer than any existing data,
// this is always true for new queries
(dehydratedAt === void 0 || dehydratedAt > query.state.dataUpdatedAt)) {
void query.fetch(void 0, {
// RSC transformed promises are not thenable
initialPromise: Promise.resolve(promise).then(deserializeData)
});
}
}
);
}
function hydrate(client, dehydratedState, options) {
if (typeof dehydratedState !== "object" || dehydratedState === null) {
return;
}
const mutationCache = client.getMutationCache();
const queryCache = client.getQueryCache();
const deserializeData = options?.defaultOptions?.deserializeData ?? client.getDefaultOptions().hydrate?.deserializeData ?? defaultTransformerFn;
const mutations = dehydratedState.mutations || [];
const queries = dehydratedState.queries || [];
mutations.forEach(({ state, ...mutationOptions }) => {
mutationCache.build(
client,
{
...client.getDefaultOptions().hydrate?.mutations,
...options?.defaultOptions?.mutations,
...mutationOptions
},
state
);
});
queries.forEach(
({ queryKey, state, queryHash, meta, promise, dehydratedAt }) => {
const syncData = promise ? tryResolveSync(promise) : void 0;
const rawData = state.data === void 0 ? syncData?.data : state.data;
const data = rawData === void 0 ? rawData : deserializeData(rawData);
let query = queryCache.get(queryHash);
const existingQueryIsPending = query?.state.status === "pending";
const existingQueryIsFetching = query?.state.fetchStatus === "fetching";

if (query) {
const hasNewerSyncData = syncData && // We only need this undefined check to handle older dehydration
// payloads that might not have dehydratedAt
dehydratedAt !== void 0 && dehydratedAt > query.state.dataUpdatedAt;
if (state.dataUpdatedAt > query.state.dataUpdatedAt || hasNewerSyncData) {
const { fetchStatus: _ignored, ...serializedState } = state;
query.setState({
...serializedState,
data
});
}
} else {
query = queryCache.build(
client,
{
...client.getDefaultOptions().hydrate?.queries,
...options?.defaultOptions?.queries,
queryKey,
queryHash,
meta
},
// Reset fetch status to idle to avoid
// query being stuck in fetching state upon hydration
{
...state,
data,
fetchStatus: "idle",
status: data !== void 0 ? "success" : state.status
}
);
}
if (promise && !existingQueryIsPending && !existingQueryIsFetching && // Only hydrate if dehydration is newer than any existing data,
// this is always true for new queries
(dehydratedAt === void 0 || dehydratedAt > query.state.dataUpdatedAt)) {
void query.fetch(void 0, {
// RSC transformed promises are not thenable
initialPromise: Promise.resolve(promise).then(deserializeData)
});
}
}
);
}
2 Replies
optimistic-gold
optimistic-gold2mo ago
can you please create a GitHub issue for this so we can track this
rival-black
rival-blackOP2mo ago
GitHub
Query options not persisted during hydration/dehydration · Issue #...
Describe the bug Setting query options like gcTime or staleTime seem to get lost on the client and replaced with default values after hydration. Your minimal, reproducible example https://codesandb...

Did you find this page helpful?