-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 currentData
property to hook results.
#1500
Conversation
✔️ Deploy Preview for redux-starter-kit-docs ready! 🔨 Explore the source changes: efbf1cf 🔍 Inspect the deploy log: https://app.netlify.com/sites/redux-starter-kit-docs/deploys/613db80528a9c00007a3cd35 😎 Browse the preview: https://deploy-preview-1500--redux-starter-kit-docs.netlify.app |
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 efbf1cf:
|
size-limit report 📦
|
@@ -420,6 +436,7 @@ const queryStatePreSelector = ( | |||
return { | |||
...currentState, | |||
data, | |||
currentData: currentState.data, |
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.
Correct me if I am wrong, but wouldn't this be basically the equivalent of doing:
const currentData = isFetching ? undefined : data;
in our own code? I would think you'd need to examine the args
associated with lastResult
to see if they are shallowly equal to the args of currentState
, something like:
currentData = currentState.data ?? (lastResult?.arg === currentState.arg ? lastResult?.data : undefined)
if the comment
this property will always contain the received data from the query, for the current query arguments
is to be true. (i.e., distinguishing cases of polling from cases of arg changes).
But, correct me if I am wrong here. I am not too familiar with the internal workings of this codebase.
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.
currentState
is always for the currently selected endpoint and lastResult
is the last result the hook had cached. So lastResult
can also be from another arg
, but currentState
never can.
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.
@phryneas Right... but my question is: this solution doesn't seem to distinguish between polling and arg changes.
The behavior I'd expect for currentData
is that if the args haven't changed (e.g., because of polling), then it would use the last result for those args (which might be cached in lastResult in that case, if a refetch is currently taking place).
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.
Data once present for the current args will not disappear from the cache while polling, and this essentially gives you data for the current args directly from the cache.
Once data is set in the cache, it will never be removed unless there is new data to replace it with, even while loading or an error occurs.
See the slice definition here:
redux-toolkit/packages/toolkit/src/query/core/buildSlice.ts
Lines 121 to 169 in 261410a
.addCase(queryThunk.pending, (draft, { meta, meta: { arg } }) => { | |
if (arg.subscribe) { | |
// only initialize substate if we want to subscribe to it | |
draft[arg.queryCacheKey] ??= { | |
status: QueryStatus.uninitialized, | |
endpointName: arg.endpointName, | |
} | |
} | |
updateQuerySubstateIfExists(draft, arg.queryCacheKey, (substate) => { | |
substate.status = QueryStatus.pending | |
substate.requestId = meta.requestId | |
substate.originalArgs = arg.originalArgs | |
substate.startedTimeStamp = meta.startedTimeStamp | |
}) | |
}) | |
.addCase(queryThunk.fulfilled, (draft, { meta, payload }) => { | |
updateQuerySubstateIfExists( | |
draft, | |
meta.arg.queryCacheKey, | |
(substate) => { | |
if (substate.requestId !== meta.requestId) return | |
substate.status = QueryStatus.fulfilled | |
substate.data = copyWithStructuralSharing(substate.data, payload) | |
delete substate.error | |
substate.fulfilledTimeStamp = meta.fulfilledTimeStamp | |
} | |
) | |
}) | |
.addCase( | |
queryThunk.rejected, | |
(draft, { meta: { condition, arg, requestId }, error, payload }) => { | |
updateQuerySubstateIfExists( | |
draft, | |
arg.queryCacheKey, | |
(substate) => { | |
if (condition) { | |
// request was aborted due to condition (another query already running) | |
} else { | |
// request failed | |
if (substate.requestId !== requestId) return | |
substate.status = QueryStatus.rejected | |
substate.error = (payload ?? error) as any | |
} | |
} | |
) | |
} | |
) | |
}, |
I wish I had something more insightful to add (based on my limited knowledge of the inner workings of how this library works) but for what it is worth: currentData behaves exactly as advertised in the comment that shows up in intellisense. It solves the issue I was personally having. Just tried it out in my code and can confirm. In general I really like this solution compared to other approaches like adding isLoadingFirstTimeForArg or having a keepPreviousData option in the hook (like React-Query does). |
@agusterodin out of curiosity, did you also try it out for polling situations? I would expect that |
It doesn't get reset, because also the cache entry does not disappear. But you now have the direct distinction: |
@phryneas great! If One other question: I'd expect that |
Just went back and checked, can confirm that currentData remains stable even when polling is enabled (which is desirable behavior) 👍 |
From the back of my head, yes - but please validate. |
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 makes sense to me - @Shrugsy perhaps we can add meaningful examples to the docs based on the use cases from #1339 (comment) once/if this lands?
@phryneas I've just now installed and tested it: Only other recommendation I can think of: I expect most devs will end up migrating from All-in-all, great work on this! It definitely fixes the UI bugs I've been having. Hope it can be released soon :) |
Honestly, I think it is niche and will probably also stay niche - the current behaviour is not by accident, but very much intended. I can see where it makes sense to use |
@phryneas sounds good; I disagree with the "niche" part, but whatever you name it, I will use it! Again, nice work on this. Will be looking forward to the next release 😄 |
yep, can do |
The In our case we need the For example, we want to use Loading the first page:
Loading the second page:
Note: The other option react-query doesn't has this "problem" that Example: Example 2.): |
currentData
property to hook results.currentData
property to hook results.
This would be one possible solution for #1339.
Feedback very welcome.
TODO: