-
-
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 upsertQueryData
functionality
#1720
Comments
Specifically, my use case is to cache the result from a create api call in a different cache. I can do this, but it comes at the cost of having to override serializeQueryArgs, and having to have some additional logic in the providesTags field for postById. If I could pass the response from createPost in postsClient.endpoints.postById.initiate, I could see that I have that data in the queryFn for getById, and if so, return it, caching it, and if not, call the api and cache it as normal.
|
Without the code for Generally, no, that is not supported, and so far I have not seen a use case valid enough to add first-class support for it, as it is very uncommon, there are ways around it (although clumsy) and would probably lead to lots of bugs and unexpected behaviour for users: the serialized arg is the base for the decision "do we make another request?" and many people would wonder why, even if they changed an argument, no additional request would be made. |
This is what those two functions look like.
Essentially what I'm doing with them is handling the case where instead of passing the If I could pass an additional parameter to an endpoint, I could avoid having to override the method Instead in
and then
|
I can think of some additional cases where having a place to put args that aren't part of the cache key would be useful. One might be if you needed to have different options when calling an endpoint. Perhaps something like having a different retry or backoff approach for an endpoint when called from one place, but not another. These options would have to be passed to the endpoint conditionally, and to have them share a cache and do so is not easy. |
So in that specific use case... how should As for different backoffs: imagine, two component are mounted with the same cache entry - but different backoffs. Which would win? And if the components mounted in a different order, or the first one rerendered after the second one rendered, which one would win then? Would it cause a request every time? |
Basically, there is one route to pass data to queries/mutations, that is through args. I want a backdoor to pass additional data, called extraArgs, with the difference made clear that anything passed through this backdoor will not be subject to any of the rules/side effects of args. Provides would still behave the same, my "backdoor" cached items should behave no different than one that is normally cached. I looked into the cache management utilities and I couldn't really find any way to provide what I want. I want the posts cached by id as Really, even more to my point and more direct/useful would be a method like
Backoffs may be more complicated on further consideration. |
As far as I know, people have been using |
That doesn't work for my use case. |
Fascinating, someone was reporting they were doing this. But yeah, looking at the code, it shouldn't work. I guess sooner or later we will need a |
upsertQueryData
functionality
Indeed we will, haha. Thank you for taking the time to understand my problem. |
In my RTK-Query API slice, I have single entity queries and list queries and they both return same type of objects. When I do a list query then I would like to upsert the cache for single entity queries. So this feature would help out. :) |
Another potential use case: @phryneas can you think of a way around this that wouldn't require this |
This is now available in https://github.com/reduxjs/redux-toolkit/releases/tag/v1.9.0-alpha.1 . Please try it out and let us know how it works! |
@markerikson Thanks for this release, I'm excited to try it out! So in my situation, we are sending a So here's my endpoint:
When I run this, I get an infinite loop of the |
Hmm. That... actually sorta makes sense, given how we implemented We opted to implement it as a tweak to our existing "actually start this async request" logic. That runs the entire standard request sequence, including dispatching actions... and also the cache lifecycle methods. You've got an I'm going to say this is probably a bug in the design and we'll have to rethink some of this behavior. @phryneas , thoughts? |
I don't really get what you are trying to do here. The code updates the exact same cache entry with the value it already just got? |
@phryneas it's more like the cache key we initially use is for generating the cache key that we want to use thereafter:
If a user were to keep hitting the URL with Walking you all through this it makes me wonder, if we can identify the generation token, would it be better to use a |
That seems reasonable to me, yeah |
Or at least you should check that the endpoint you want to update is not the endpoint that just has been updated - I mean, just compare the argument of the current query with the argument of the query you want to upsert. |
@phryneas that seemed like the most streamlined approach so I tried it and I could get out of the infinite loop, so thanks for that! However, I am still hitting the API for that Edit: Working on my implementation so maybe it's something on my end. |
Do you see |
I'm hoping my use-case isn't super specific to the point where this isn't helpful but let me flesh out the application a bit more: First of all, before I can even fetch the data from API, I need an Therefore, I don't make the After that lazy query, I compare the returned So the questions are:
@phryneas in response to your question, I see the |
@markerikson Thanks for your analysis. Sorry about the CRA repo, it was the end of the work day and I was sort of just trying to get something done quickly so I messed it up. In the end, I was still getting the same response even if the correct RTK version, but you saw that. @phryneas Thanks for that fix! So basically, you are skipping the child query unless the parent query is successful, that way the timing lines up that the cache has the upserted data? Very clever (I didn't know about that I do utilize this cart query all over my project but maybe I could wrap this code with a custom hook just to ensure I can render the page with the first data and in the background, make the second query. I'll give it a try and see if it works, thanks for your help! |
Super looking forward to this functionality! Before I create a minimal reproduction of a problem I am running into while trying this new feature out, let me know if I am potentially misusing it or misunderstanding its capabilities. I am running
Background: we have all the metadata for an individual "alarm" in our app ahead of time from the "alarm list" endpoint (used for the list of cards). Ideally since we already have this data I would like to prepopulate the cache on the alarm details screen so the user never sees a loading spinner other than the situation where initial page load happens to be on an "alarm details" screen. If you scrub frame by frame on the video below you will see that for a millisecond the pre-populated data appears. Unfortunately it immediately disappears and the loading state appears. Ideally the loading boolean would never be true and if the newly fetched data differs from the pre-populated data, the data would just be updated but without ever going into "loading state". Is that possible with this feature? It would be super nice to have! Is this just a bug since upsertQueryData is still in feature preview? Screen.Recording.2022-09-06.at.11.57.58.AM.mov |
@agusterodin please try again with the CI build from #2669 |
Back again. Thanks for your help so far, I've been able to de-duplicate the first fetch of my application. The problem I'm currently having is driving me nuts in that I cannot replicate it outside of my company's application in CodeSandbox so I'm resorting to asking you if this is a known issue:
Is this to be expected? Do I need to de-duplicate and then run the query again and then I can mutate that entry? Edit: Maybe a better question is how do I know when step 3 above is done? |
@davidrhoderick without code, generally hard to tell. upserting should happen almost immediately when you call that method - maybe 1 or 2 ticks later. |
I can confirm that updating the upsert implementation to 'subscribe: true' (and then unsubscribe afterwards) fixes the issue. As a design decision, Is this something that can be made configurable? Not entirely sure what the point of upsert is if you can really only update? I guess it enables subscribers that don't make API requests.. like lazy queries? (I'm probably missing something). Edit: - I really shouldn't have glossed over some of the thread above, looks like this PR fixes the issue I'm running into: #2669 - Hopefully we have a new version released as |
FYI I should hopefully be able to put out another 1.9.0 alpha tomorrow - got some other things I want to work on before I publish it. |
Just published https://github.com/reduxjs/redux-toolkit/releases/tag/v1.9.0-alpha.2 - please try it out and let us know if |
Thanks good sir! Really hyped for upsert functionality. I tried installing alpha 2 earlier today and one of my query hooks is acting weird. One of my query hooks is returning undefined data always despite response appearing completely normal in Chrome DevTools network tab. Other than the one broken query hook, all my other query hooks work fine. This issue is completely unrelated to upsert (haven't added anything related to upsert in my code just yet). Alpha 1 has this same weird issue of query hook not working. Downgrading to latest stable version causes everything to work perfectly again. I will narrow it down and provide a repro tomorrow if it isn't some sort of trivial mistake. |
Hmm. We've definitely moved around a lot of internal pieces on the 1.9 branch, so it's entirely likely we borked something :) Yeah, a repro/sandbox/replay would be very helpful! |
The issue with the query hook only occurs on a page of mine that renders the page via SSR when that is the initial page you start using the website from. This is to make it so that the site doesn't break for web crawlers .
I can confirm that the console log statement only ever gets printed when you open the web app from this particular page (in NextJS). The query hook is still broken even if I comment all code within the getInitialPageProps function like this ->
If I comment out the whole thing like this, my useBlaQuery hook works perfectly again ->
Does this ring any immediate bells? Can you try pasting the snippet into one of the pages of your Next projects to see if your query hooks break as well?
I'm not ruling out that my Redux setup/configuration isn't bum. Will create a reproduction if the issue doesn't immediately jump out at you after seeing this. About to do some meal prep for the week and then i'll hop back on to create that repro. |
Btw, I commented out the getInitialProps stuff and tried upsert functionality. Upsert works exactly as expected! :D |
I'm gonna guess that if you have the Can you take a look what exactly is modified in the state through that action? |
Here is a minimal reproduction. For all I know I am not setting up Redux store properly to handle hydration. That being said, I'm surprised it worked in last stable version of RTK. I really appreciate the help! Leaving the https://github.com/agusterodin/redux-toolkit-1.9-alpha-ssr-bug-reproduction |
I had to do some changes to this to make it run at all for me (add node-fetch and abort-controller, change the api to reqres.in), but then it works on my system. Could you maybe tweak this so it works on a CodeSandbox so that we really can both see the same behaviour? |
Will do as soon as I get back home tonight. Are you on a version of Node below 16? Mine works fine without the node-fetch and abort-controller dependencies. Dumb move on my behalf not to just use a public mock API 😳 |
I just updated my reproduction repository to use reqres.in and pushed changes. Unfortunately CodeSandbox isn't working properly and I can't create one (I keep getting an authorization error regardless of what I try) This is the behavior I am experiencing when running locally. The data doesn't fetch if I start on the index page and click the link. The data does fetch if I start on the /image page (due to SSR doing its thing). Screen.Recording.2022-10-11.at.6.47.21.PM.movI am on Node v16.13.0. |
Were you able to recreate the issue with my updated issue reproduction repository? Is there anything else I can do to help with debugging? |
Honestly haven't had time to look at it. My last couple evenings have been focused on the codemods for the I may be able to take a look over the weekend. |
@agusterodin You can just replace https://codesandbox.io/s/github/agusterodin/redux-toolkit-1.9-alpha-ssr-bug-reproduction I see the problem, investigating now. |
@agusterodin the problem here is a timing problem in combination with that hydrating reducer you are using. This works: const reducer = (state, action) => {
if (action.type === HYDRATE) {
+ const { api: _ignore_and_let_RTK_handle_this, ...hydrate } = action.payload;
const nextState = {
...state,
- ...action.payload
+ ...hydrate
}
if (typeof window !== 'undefined' && state?.router) {
nextState.router = state.router
}
return rootReducer(nextState, action)
} else {
return rootReducer(state, action)
}
} Generally, it is safer to have RTK Query do the rehydration for the api slice itself. In this specific case, there is nothing to hydrate from the server. So that reducer overwrites the client state with I believe this might have something to do with a timing change that seems to have happened in a recent version of |
Yeah... reading export default wrapper.withRedux(App) to function App({Component, ...rest}){
const {store, props} = wrapper.useWrappedStore(rest);
return (
<Provider store={store}>
<Component {...props.pageProps} />
</Provider>
);
} and they cannot really do that before first render. ( That said, it is highly problematic. Before that change, even @agusterodin's "overwrite everything" rehydrate reducer would not be a problem. After it... well, it breaks. |
My takeaway from those last couple comments is that whatever @agusterodin was seeing isn't actually related to Given that, I'd like to close this issue for bookkeeping purposes. If there is anything else going on, let's open up a new issue from here. 1.9-beta.0 is ready to go - I'm just going to wait until morning to publish and announce it. |
Yes, my previously mentioned issue is 100% related to the next wrapper and ended up having nothing to do with RTK 1.9. Downgrading to wrapper v7 fixes the issue. Upsert behavior works exactly as expected 🎉 Thanks for the incredible work! |
Suh-WEEEEET! Thank you for the fast response and for trying that out for us! |
@phryneas @markerikson Just wanted to say thanks a lot for release 1.9.0! It looks like it's working great on my application. I had to do a couple things to get it working but they were necessary for my application:
Plus the fact that you've now officially released this code as version 1.9.0, I have to say great job! |
I have a use case where I would like to pass an additional parameter when calling an endpoint, where I do not want this additional parameter to be cached. The first parameter, args, would still be serialized as the cache key as normal.
Would it be possible to provide an additional field to pass some additional data when calling an endpoint via a hook/initiate? My use case is for calling a query endpoint with initiate.
Perhaps an additional optional parameter after options? Something like
extraArgs
,noCacheArgs
, ornonCacheableArgs
. I don't know if it makes sense have it on the options parameter, https://redux-toolkit.js.org/rtk-query/api/created-api/endpoints#initiate, but that would work too.The text was updated successfully, but these errors were encountered: