-
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
Implement minimal useFragment
hook
#8782
Conversation
I wanted this ~2 weeks ago, love to see it! |
Super excited for this! Part of a team of ~10 devs who are currently using my very-disfunctional useFragment 😅 |
cd14628
to
92b140a
Compare
65dcb4c
to
61f1edf
Compare
1111b8e
to
0a629c1
Compare
0a629c1
to
8f02de3
Compare
18addd5
to
020ad25
Compare
Since we are postponing A rebased-and-ready-to-cherry-pick version of that commit (35ccbd7) can still be found on this branch. |
Hi @benjamn , |
// from: string | StoreObject | Reference; | ||
// fragment: DocumentNode | TypedDocumentNode<TData, TVars>; | ||
// fragmentName?: string; | ||
// optimistic?: boolean; |
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.
what is optimistic
used for.
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.
InMemoryCache
can temporarily have additional layers during mutations that use optimisticResponse
, which then get discarded once the real data have been written to the root/non-optimistic layer of the cache.
When you're using useFragment
, you can control whether it reads data from these optimistic layers (optimistic: true
, the default), or just the root layer (optimistic: false
). Reading from optimistic data is a good default because it's the same as reading non-optimistic data when there are no active optimistic updates, and reading optimistic data allows the optimisticResponse
to be rendered immediately, and then the real response only needs to trigger a UI update if the optimisticResponse
was wrong/incomplete.
| "id" | ||
> { | ||
from: StoreObject | Reference | string; | ||
// Override this field to make it optional (default: true). |
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.
Why do we need to specify "from", since we already specify 'fragment`. I am guessing because we don't know which query it is bound to at runtime, and the 'id' of the type.
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 options.from
value represents the specific entity object (with some __typename
that matches the fragment type condition, and some unique ID field(s)) against which the options.fragment
should be applied/executed.
When you execute a query rather than a fragment, there's no mystery about which object to start with, since there's only one singleton ROOT_QUERY
object. Fragments don't have that luxury, and always need to know the starting object. Occasionally it might be possible to guess the object based on the fragment's type condition; for example, fragment QueryFrag on Query {...}
is always going to be a fragment that starts executing against the ROOT_QUERY
object, but I'm not sure those special cases are worth exploiting, since they are not the common case.
The options.from
argument is similar to the options.id
field for cache.readFragment
and cache.writeFragment
, but a little more flexible (in case you don't know the string ID).
@Lalitha-Iyer Thanks for the review! My two biggest open questions are:
When you asked about paginated fragments, were you thinking of something like putting a |
Thanks for the clarifications @benjamn !
I didn't mean to suggest any implementation, I was curious to know the breadth of capabilities we plan to make available long term. As a contrived example, one way to think abt the scope would be: what is the lifecycle of a fragment and what controls will be available to manage fragments ( accessing already cached data -> subscribing to changes -> refetching/pagination -> garbage collection). With the lifecycle/capabilities listed, we could then try to model fragments either in the current way ( loosely coupled from queries ) or a stricter 1:1 mapping or a 1:many ( depending on whichever approach solves our usecases). |
@benjamn ignore my previous comment, I probably misunderstood fragments. Reading the Graphql spec if fragments are a reusable set of fields that can be applied to any query then your approach of not binding to a query makes senss. Your open questions around error handling and at runtime mapping fragment to one/more queries seem reasonable. Thanks again for taking time to clarify. |
#8782 (comment) Previously, the ListFragment was effectively ignored because its type condition (on Query) did not match the "Item" __typename. Although the code was definitely wrong before, this change ends up not making a visible difference because the Item.id field provided by ListFragment is the default field used for normalization, so it gets picked up and used regardless of whether it's mentioned in the query (even when ListFragment is completely ignored, as it was before). To make this change matter in a visible way, I added an extra root query field to ListFragment to strengthen the tests.
Although useFragment increases bundle sizes by this measurement, it also provides an alternative to useQuery and useLazyQuery that should prove smaller (if used alone, or with `useBackgroundQuery`) than using the existing hooks.
Initial implementation of the idea I sketched in #8236 (comment) (though without
previousData
for now).This API is still in development and is very much subject to change (hence the [WIP] tag). Watch this space for more details and examples in the coming days/week.