Skip to content
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

fix: no hydration when new promise comes in #8383

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

juliusmarminge
Copy link
Contributor

No description provided.

Copy link

nx-cloud bot commented Dec 2, 2024

View your CI Pipeline Execution ↗ for commit 3c1b221.

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ❌ Failed 5m 1s View ↗

☁️ Nx Cloud last updated this comment at 2025-01-12 18:18:35 UTC

Copy link

pkg-pr-new bot commented Dec 2, 2024

Open in Stackblitz

More templates

@tanstack/angular-query-devtools-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-devtools-experimental@8383

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@8383

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@8383

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@8383

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@8383

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@8383

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@8383

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@8383

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@8383

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@8383

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@8383

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@8383

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@8383

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@8383

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@8383

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@8383

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@8383

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@8383

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@8383

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@8383

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@8383

commit: 3c1b221

@juliusmarminge
Copy link
Contributor Author

really weird, queryFn is getting called and returns the updated value, but the serializer is called only with teh initial count

CleanShot 2024-12-02 at 17 16 59

@juliusmarminge
Copy link
Contributor Author

juliusmarminge commented Dec 2, 2024

before digging deeper, can you confirm if this is a bug or expected @TkDodo ?

I think we might not be in a pending state so the promise doesn't get sent down?

https://github.com/juliusmarminge/query/blob/cf37452e72e44609e49600f8b7c124cc5a197af5/packages/query-core/src/hydration.ts#L83-L92

Comment on lines 1121 to 1125
// --- server ---
countRef.current++
const promise2 = serverQueryClient.prefetchQuery(query)

const dehydrated2 = dehydrate(serverQueryClient)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assume this is a server fn calling revalidatePath() so the page re-renders

@TkDodo
Copy link
Collaborator

TkDodo commented Dec 2, 2024

you’re right: we only send the promise when we are in pending state. The fix is likely to send the promise when we are in fetchStatus: 'fetching' instead 💡

@juliusmarminge
Copy link
Contributor Author

i believe another issue is we're getting dataUpdatedAt: 0 so it does't rehydrate

CleanShot 2024-12-02 at 19 54 15@2x

@juliusmarminge
Copy link
Contributor Author

feels like we need some other state that would let us know if the promise is fresher then we need to set the queries' promise or something.. this goes deeper than I thought

@TkDodo
Copy link
Collaborator

TkDodo commented Dec 2, 2024

I'd say if we get a promise, we should always assume it's newer and hydrate its data, no?

@juliusmarminge
Copy link
Contributor Author

juliusmarminge commented Dec 2, 2024

how do we check for that wihtout causing infinite re-renders in <HydrationBoundary> ?

@juliusmarminge
Copy link
Contributor Author

juliusmarminge commented Dec 2, 2024

struggling to figuring out how to properly filter out the queries to put in the hydration queue

CleanShot.2024-12-02.at.21.01.26.mp4

@TkDodo
Copy link
Collaborator

TkDodo commented Dec 3, 2024

@juliusmarminge have you widened this check?

const hydrationIsNewer =
dehydratedQuery.state.dataUpdatedAt >
existingQuery.state.dataUpdatedAt

to something like:

const hydrationIsNewer =
  dehydratedQuery.state.dataUpdatedAt >
    existingQuery.state.dataUpdatedAt || dehydratedQuery.promise

so that whenever a promise is included, we put it the queue.

@juliusmarminge
Copy link
Contributor Author

yea that causes infinite rerenders

@TkDodo
Copy link
Collaborator

TkDodo commented Dec 3, 2024

interesting; let’s debug that together today if you have some time?

@juliusmarminge
Copy link
Contributor Author

juliusmarminge commented Dec 3, 2024

interesting; let’s debug that together today if you have some time?

sure! what time? I can sneak in a short seesion anytime after 10am gmt+1.

@TkDodo
Copy link
Collaborator

TkDodo commented Dec 3, 2024

I pinged you on discord ;)

existingQuery.state.dataUpdatedAt
existingQuery.state.dataUpdatedAt ||
// @ts-expect-error
dehydratedQuery.promise?.status !== existingQuery.promise?.status
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might actually be working

Copy link
Collaborator

@Ephem Ephem Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we prefetch a query without await in a page that is static or cached, either fully or partially, and then client navigate to it with newer data in the cache? My guess is that there are/can be situations where we get a promise that will resolve to data that is actually older than the one we have on the client already?

Maybe the entirely correct way to do this would be to inspect the data the query resolves to before determining whether to update the cache with that data? Implementation-wise that's a lot tricker though unfortunately. :(

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the entirely correct way to do this would be to inspect the data the query resolves to

this is something only users could verify though. Without query meta-data like dataUpdatedAt, react-query doesn’t know the age of the data. Are you suggesting we provide a way for users to extract a timestamp (age) from the promise data in some sort of callback option?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it? If we are using the fetch timestamp from the server to determine this when hydrating data, we can surely do that for promises too, just after they resolved? I'm sure it might be a big painful thing to implement, but is there some inherent thing about user/library land that blocks us from doing this in the library using the same logic?

To be clear, if we already have this query in the cache, this might require us to wait for the promise outside of the cache and only put the data in. Or even worse, to support fetching states properly, it might requires us to have some sort of "possibleUpdatePromise" or something that would not commit it's result to the cache if it's older.

@TkDodo
Copy link
Collaborator

TkDodo commented Dec 29, 2024

@juliusmarminge what are we gonna do with this one?

@juliusmarminge
Copy link
Contributor Author

Sorry completely forgot about this 😅

I think I had it sort of working but happy to jump on a call and talk about it still! Hit me up on discord when you have some time to go over it!

@TkDodo TkDodo marked this pull request as ready for review January 12, 2025 16:19
otherwise, we risk including promises that happen because of background updates (think persistQueryClient)
otherwise, we are re-using the cache and the query won't be in "pending" state the second time around
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants