-
-
Notifications
You must be signed in to change notification settings - Fork 642
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(query): subscribe instead of fetch if initialData #586
Conversation
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/pmndrs/jotai/45V3uyoqf5pbyiV4LG2wkzEz3iqv |
4d92961
to
e8310d0
Compare
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit ab36eaa:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approach seems valid: if we don't store the initial promise, it will mount, so fetchOptimistic is not required.
Is it possible to add failing tests? I'm not super confident to keep the behavior when we do some refactors.
src/query/atomWithQuery.ts
Outdated
.fetchOptimistic(defaultedOptions) | ||
.then(listener) | ||
.catch((error) => listener({ error })) | ||
if (!defaultedOptions.initialData) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More precisely, you want to check if getInitialData()
returns non-falsy value. Like, options.initialData = () => null
. We may drop the support of it. Just needs to make it consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, that is a good catch. Do you think this would be sufficient?
if (!defaultedOptions.initialData) { | |
if (!getInitialData()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should call getInitialData()
just once in our code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, agreed!
@@ -301,5 +302,7 @@ it('query with initialData test', async () => { | |||
|
|||
// NOTE: the atom never suspends | |||
await findByText('count: 0') | |||
expect(mockFetch).toHaveBeenCalledTimes(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dai-shi does this work for you with regards to testing? I can't think immediately a way to create a failing test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean this doesn't fail before the fix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the change. It looks good to me.
Is anyone else interested in trying and/or reviewing?
observer | ||
.fetchOptimistic(defaultedOptions) | ||
.then(listener) | ||
.catch((error) => listener({ error })) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dai-shi I don't think we need this bit of code either way it seems -- can you tell me more about it?
Because we are subscribing on mount, the subscription kicks off the fetch initially. Seems like we're doing double work, unless I am missing something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an atom suspends, the component suspends and it will not be mounted, which means onMount
will not be called. Unless we resolve the promise, the component will not be mounted.
While doing some more testing, I noticed that on mount a query would automatically refetch even though I had provided
initialData
to it. I could not stop this behavior -- based off of reading https://react-query.tanstack.com/guides/initial-query-data#_top, it is the case that initialData will be treated as fresh until the staleTime has been met, or based on other user options passed (meaning a fetch should not happen until it become stale, and there have been some refetch options passed)Because we were automatically running the fetchOptimistic method regardless of if initialData was passed, we were causing an unneeded fetch to happen. React query will pick up and take over and do all the fetching if needed when there is initialData. We are already subscribing on mount, so there is no need to fetch during the initial get.