-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Add a new useLoadableQuery
hook
#11300
Conversation
🦋 Changeset detectedLatest commit: c20e373 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
size-limit report 📦
|
916b4f3
to
94a424f
Compare
This comment was marked as resolved.
This comment was marked as resolved.
useInteractiveQuery
hookuseInteractiveQuery
hook
useInteractiveQuery
hookuseLoadableQuery
hook
/release:pr |
A new release has been made for this PR. You can install it with |
4f6b316
to
5678b88
Compare
/release:pr |
A new release has been made for this PR. You can install it with |
I think this hook should also include a |
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.
This looks very good!
I've left some small nitpicks/ideas, but nothing major :)
data: { hello: "from cache" }, | ||
networkStatus: NetworkStatus.ready, | ||
error: undefined, | ||
}); |
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.
Maybe add something like expect(ReadQueryHook).not.toRerender()
to make sure nothing else will come in after?
expect(SuspenseFallback).not.toHaveRendered(); | ||
}); | ||
|
||
it('suspends and does not use partial data when changing variables and using a "cache-first" fetch policy with returnPartialData', async () => { |
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.
it('suspends and does not use partial data when changing variables and using a "cache-first" fetch policy with returnPartialData', async () => { | |
it('suspends and does not use empty partial data when changing variables and using a "cache-first" fetch policy with returnPartialData', async () => { |
this confused me a bit at first, but you mean it won't use a non-existing/empty entry as "partial data", right?
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.
Ya this could use some clarifying behavior here. The idea with this test is that when returnPartialData
is true
and I have partial data for 1 set of variables, it didn't try and reuse that partial data for a different set of variables. It should start with a clean slate and suspend to fetch the entire data set.
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.
Any way you can get that in there? It makes sense now, but didn't get too obvious from the description, or the test in combination with the description. Could also just be a long comment.
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.
Yes! Updated in b84f0d5. Hope this helps clear things up :)
src/react/hooks/useLoadableQuery.ts
Outdated
[queryRef] | ||
); | ||
|
||
const loadQuery: LoadQuery<TVariables> = React.useCallback( |
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.
Crazy thought:
Could we maybe prevent this function from being executed before the first render committed by throwing an exception?
It might prevent some abuse scenarios where other hooks might be better suited 🤔
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 like that idea. I believe Relay does something similar with its useQueryLoader
hook where it prevents you from calling its function during any render, so its definitely a pattern out there in the wild today.
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.
Added in 9a6c748. Fair warning, it might be a tad controversial. BUT Relay has something similar, so I felt it was ok.
… specifying used options
af0234b
to
383743d
Compare
496ccfe
to
5c76e4f
Compare
@phryneas this should be fully updated with the updated profiler API and is ready for another look! |
/release:pr |
A new release has been made for this PR. You can install it with |
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'm through all the files, but only halfway through the tests.
I'm already submitting my accumulated comments since we have meetings coming up now.
LoadQueryFunction<TVariables>, | ||
QueryReference<TData> | null, | ||
{ | ||
fetchMore: FetchMoreFunction<TData, TVariables>; |
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.
fetchMore: FetchMoreFunction<TData, TVariables>; | |
/** {@inheritDoc @apollo/client!ObservableQuery#fetchMore:member(1)} */ | |
fetchMore: FetchMoreFunction<TData, TVariables>; |
fetchMore
actually doesn't have a comment in ObservableQuery
yet, so this might be a good time to add one? ^^
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'm going to hold off on this update for a followup PR. I'd like to hit useSuspenseQuery
and useBackgroundQuery
as well with this change. This allows me to keep this PR focused on useLoadableQuery
as much as possible.
QueryReference<TData> | null, | ||
{ | ||
fetchMore: FetchMoreFunction<TData, TVariables>; | ||
refetch: RefetchFunction<TData, TVariables>; |
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.
refetch: RefetchFunction<TData, TVariables>; | |
/** {@inheritDoc @apollo/client!ObservableQuery#refetch:member(1)} */ | |
refetch: RefetchFunction<TData, TVariables>; |
The refetch
comment of ObservableQuery
doesn't read perfect for the hooks, but it might at least be a starting point until we have some central interface with all the hook options to pick from
(it reads:)
/**
- Update the variables of this observable query, and fetch the new results.
- This method should be preferred over
setVariables
in most use cases.- @param variables: The new set of variables. If there are missing variables,
- the previous values of those variables will be used.
*/
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.
Love it! Will get this in a followup PR to ensure useBackgroundQuery
and useSuspenseQuery
can benefit as well.
// Resetting the result allows us to detect when ReadQueryHook is unmounted | ||
// since it won't render and overwrite the `null` | ||
Profiler.mergeSnapshot({ result: null }); | ||
|
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.
// Resetting the result allows us to detect when ReadQueryHook is unmounted | |
// since it won't render and overwrite the `null` | |
Profiler.mergeSnapshot({ result: null }); | |
Profiler.mergeSnapshot({ queryRef }); |
What do you think about tracking the queryRef
here instead?
Also, this makes me think... Maybe we should also track unmountedComponents
or internally track which components are currently mounted and which are not, so we can make assumptions on that?
This could happen internally in useTrackRender
const { tree } = await Profiler.takeRender();
expect(Component).toBeMountedIn(tree)
expect(Component).not.toBeMountedIn(tree)
expect("ComponentName").toBeMountedIn(tree)
expect("ComponentName").not.toBeMountedIn(tree)
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 was thinking the same. I hit this case at the end of the day yesterday and figured I'd revisit today. It's not obvious when something has unmounted so something like this would be much nicer. I love the matcher you have here as well so I'm borrowing that.
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'm going to do this in a separate PR so we can provide some good discussion if need be and move this PR forward.
Closes #11349
Introduces a new
useLoadableQuery
hook. This hook works similarly touseBackgroundQuery
in that it returns aqueryRef
that can be used to suspend a query viauseReadQuery
. Its goal is to provide a more ergonomic way to load the query during a user interaction (for example when wanting to preload some data) that would otherwise be clunky withuseBackgroundQuery
.