-
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
Consider the behavior of useSuspenseQuery
when non client/query/variables options change
#10680
Comments
We are creating a new API here, so let's consider what would result in the least amount of bugs & confusion. I suggest removing the arguments to the I might even suggest removing the These methods always create a ton of ambiguity on how things should work with them, and they are the source of many conflicts - and as a result, of bugs. Instead, we could try to embrace React here: if users want to change their variables/options/etc., they can maintain them themselves in a We could even offer a helper hook for that. So, we would end up with scenarios where users would have very granular control over what happens and when. In light of that, we could revisit the question on "when and how to refetch" that you posed - and the answer could be one of:
I believe 2. would be problematic with variables passed in from a parent component, so I suggest we go with 1. The question of "suspend or not" is also an interesting one. I'd almost say "always suspend" - this is |
I've actually considered this as well, however I haven't fully committed to this yet for a few reasons. I'll address both, but let me take them one function at a time.
It appears Relay does have some form of a refetch with new variables, so this pattern isn't totally foreign.
This function is pretty low overhead and it doesn't interact with Suspense anyways. There is no "loading" state here, rather the connection is either opened and listening for subscription payloads or its not.
Would you be able to provide an example here? I'd like to understand this a bit more. Are you thinking its two components using data from a parent
I agree, it causes some interesting situations. One of the examples that came to mind is what happens if you That being said, it makes me wonder how much of this problem also exists in other hooks such as
I absolutely agree. I want users to be able to leverage any and all tools that React provides ( The main reason I opened this issue though is in the most recent refactor, regardless of how
I'd be curious what this might look like. I'm not fully convinced we need a separate hook, but I'd like to understand your idea here more before I shrug this off. Perhaps some code samples?
This x1000. I absolutely agree. No reason we should avoid suspending when loading data. Its the reason I'd like to get #10676 done so that I can remove that Really I'd just like to figure out if there are any other options that, when changed, should cause the hook to fetch, or whether these options are "silently" applied and will be set for behavior moving forward (for example, any fetch after changing Anyways, thanks so much for these thoughts! Would love to get a bit more info from you on a couple of your ideas. |
I meant to comment on this as well, but you're absolutely right. This is actually a very important point and I don't want to lose sight of this. I think we should have the freedom to APIs that may or may not line up with other parts of Apollo since this is a totally new paradigm. I tended to err on the side of maintaining a familiarity with |
Phew, tons to unpack here :) refetch / changing variables
My thought was actually to change the refetch behaviour in a way that it would trigger a rerender and then guarantee a refetch during the next render. Due to Reacts auto-batching that would allow you to do something like setVariables(newVars);
refetch(); //maybe rename it to `queueRefetch`? and it would just go the "normal React way", while keeping the "state-keeping" out of our implementation. Our implementation would probably need a Something like const lastRefetch = useRef();
const [nextRefetch, forceRefetch] = useReducer((x) => x + 1);
if (lastRefetch.current !== nextRefetch) {
lastRefetch.current = nextRefetch;
// trigger refetch
} fetchMore
I had naively assumed that even with a subscribeToMore
This would be my main question: how much overhead is this to have in and maintain it in the future? If it's not a lot, okay, but please make sure that it's not a starter for feature creep. Worst case we have one more hook, but didn't overload
I am thinking of two independent components calling both
Nothing specific to be honest. More a reminder that when we decide to holding "mutating internal state" within
I guess that's the main point of my comment. I cannot really tell you a lot on the initial question, what should refetch when, as I'm just not deep enough into that yet - but I can very well try to play devil's advocate and as "how much of this do we need and how much of it is just inherited?" :) |
FYI I just opened a PR (#10700) that addresses this issue. Currently yes, a |
Done in #11025 |
With the refactor for
useSuspenseQuery
in #10672, we lost the ability to update thewatchQueryOptions
on theObservableQuery
when changed between renders (i.e. new props passed into the hook). Before the refactor, changes toquery
and/orvariables
would result in passing the wholewatchQueryOptions
toreobserve
, thereby updatingObservableQuery
with those options. With the refactor, theObservableQuery
lifecycle is now handled outside React with no mechanism to changewatchQueryOptions
. It appears none of this behavior was tested in the current test suite. That might be ok because this gives us an opportunity to determine what the correct behavior should be (NOTE:client
,query
, andvariables
are not included as changes to these are properly propagated with refetches).Because we are using React Suspense, there are additional considerations to take into account. Traditionally
watchQueryOptions
are updated through the methodObservableQuery.reobserve
, which has the potential to kick off a new network request (for example, whenfetchPolicy
changes tonetwork-only
orno-cache
). In a Suspense world, new network requests have the possibility of re-suspending the component. Depending on the granularity of the user's Suspense boundary, this could be jarring to an end user.To contrast,
useQuery
uses this technique to update options. It however has 2 benefits: 1. It supportsnotifyOnNetworkStatusChange
as an option, which whenfalse
will prevent rerenders for new loading states and 2. Loading states are done inline, so even whennotifyOnNetworkStatusChange
istrue
, it limits the blast radius of the loading state.I see a few paths forward:
cache-only
fetchPolicy
wouldn't fetch, but for the sake of the argument, I'm choosing to keep it simple). With the technique, we'd likely want to recommend using React'sstartTransition
API for users that want to avoid suspending and show the existing UI until loaded.ObservableQuery
, which would take effect for results going forward. For example, changes to theerrorPolicy
would only affect future requests.ObservableQuery
from the initial render are set in stone and changes to them do not affect anything going forward. This is the most simple, but has the potential to confuse our users.To add more nuance to the discussion, this may be very option dependent. It's possible we could use a combination of all 3 paths mentioned above and choose which technique to apply to each option. For example, should changing the
errorPolicy
between renders cause the query to refetch? My take is no: this should only affect network requests going forward, so this feels like something that should be silently updated. What about changing thefetchPolicy
? I could see the argument where this could warrant a potential refetch.I think it would be helpful to go through each option and determine how changes to that option would affect the behavior.
Supported options
Here is the list of currently supported options. For brevity, I have omitted
client
,variables
, andsuspenseCache
as changes to these options propagate as intended: They will execute a new request when changed.NOTE: This list may change between now and final release, so the list captures the currently supported options at the time of this writing. I'll do my best to keep this updated if we add/remove options before this issue is completed.
errorPolicy
context
canonizeResults
returnPartialData
refetchWritePolicy
fetchPolicy
The text was updated successfully, but these errors were encountered: