Closed ghasemikasra39 closed 3 years ago
You can use updateQueryData
, but be aware that the next time that query is being refetched from the server, all your changes will be gone.
The job of RTK-Query is to accurately reflect the last known state of the server. If you are keeping local property changes for it, it might be a good idea to keep track of those in parallel in a dedicated slice.
Dear @phryneas
Does it work for a mutation
query as well? Because I am trying to make it to work, but my UpdateRecipe
function is not called:
const playgroundApi = createApi({
reducerPath: 'playgrounds',
baseQuery: fetchBaseQuery({baseUrl: Global.baseUrl}),
endpoints: builder => ({
getPlaygrounds: builder.mutation({query: getPlaygroundsHandler})
})
})
import {useDispatch} from 'react-redux';
const dispatch = useDispatch()
const updateQuery = playgroundApi.util.updateQueryData('getPlaygrounds', undefined, (draft) => {
console.log('It is not called !!')
draft.push({ id: 1, name: 'Teddy' })
})
const patchCollection = dispatch(updateQuery)
No, just for queries, as mutation results are "throwaway" and not long-living cache entries.
I am pretty sure that something called getPlaygrounds
should be a query though - why is it a mutation in your case?
Dear @phryneas
Yes, you're right, getPlaygrounds
should be a query. Thank you for your help, you saved my day. I will close this issue.
@phryneas Can you elaborate on why mutation results are throwaway?
Let's say there are two endpoints GET /api/profile
and PUT /api/profile
. The latter returns the updated user profile.
Is there a way for RTK Query to use that response and update the cache, or does the updated profile have to be re-fetched via the GET
endpoint?
You can use optimistic updates to update that other endpoint - but refetching is surely less work.
As for mutations being throwaway. Imagine you have two components, A and B. Both trigger the same mutation (let's assume something like spending money and getting a receipt id back) and show the results. Now you would not expect the receipt id from the money sending in component A to suddenly override the value in component B.
That is, why a mutation result is tightly bound to the hook instance that triggered it - and if the parent component of that hook unmounts or that hook just triggers another mutation, there is no real way of "getting" that result, since it is cached by request id. So it is removed at that moment.
@bkempe I haven't tried it, but you could probably use the cache lifecycle methods in association with myApi.util.updateQueryData
to do it:
See these references:
Thanks!!
Is there a way to check why the function I pass to api.util.updateQueryData
is not get called?
this is what I get after dispatching my updateQuery:
{
patches: []
inversePatches: []
undo: ƒ undo()
__proto__: Object
}
and I don't see any queryResultPatched
action being dispatch!
It does not get called only if there is no cache value to update.
But I can see the cache value using my RN Debugger. I can see the respective piece of state:
export const scrollingNoteApi = createApi({
reducerPath: 'scrollingNote',
baseQuery: fetchBaseQuery({
baseUrl: Global.baseUrl,
prepareHeaders: async (headers, {getState}) => {
const token = await AsyncStorage.getItem('token')
headers.set('authorization', `Bearer ${token}`)
return headers
}
}),
endpoints: builder => ({
getScrollingNote: builder.query({query: getScrollingNoteHandler}),
})
})
const action = scrollingNoteApi.util.updateQueryData('getScrollingNote', undefined,
(draftNotes) => {
draftNotes.push({'new': 'data'})
})
const res = dispatch(action)
That seems to have been called with a different argument than undefined
. Your code would update the cache entry for useGetScrollingNoteQuery()
or useGetScrollingNoteQuery(undefined)
. That does not seem to exist.
You are right, I am using it this way:
const {data, isLoading, refetch} = useGetScrollingNoteQuery(notesList)
okay so firstly, I fixed it by passing notesList
(I still don't understand why):
const action = scrollingNoteApi.util.updateQueryData('getScrollingNote', notesList,
(draftNotes) => {
draftNotes.push(...newNotes)
})
So I think I still did not fully understand updateQueryData
. Honestly, I read this section multiple times, but still don't understand the usage of args
:
args: a cache key, used to determine which cached dataset needs to be updated
When I am using getScrollingNote
, does not RTK understand which cached dataset needs to be updated? I mean, I am explicitly mentioning getScrollingNote
.
Is args
used by RTK Query internals or can it be used by end-users? I mean, I really don't understand what I it expects for this argument. I saw it was always passed as undefined
in the doc.
Assume you use
useGetScrollingNoteQuery(5)
and useGetScrollingNoteQuery(4)
. Then you have two cache entries in the store at the same time.
Calling
const action = scrollingNoteApi.util.updateQueryData('getScrollingNote', 4, ...
will update one and
const action = scrollingNoteApi.util.updateQueryData('getScrollingNote', 5, ...
will update the other
I got it now. But why can I have two cache entries of the same piece of state in the store at the same time? Redux is said to be so-called Single Source of Truth
, so when changing a piece of state in redux via an action, regardless of who and from where the action is dispatch, and regardless of the action effects, should update the value for all subscribers, right?
RTK Query is a document cache, not a normalized cache. That data is stored as-is and everything is treated as completely independent data.
Building a normalized api cache that takes all the infinite possible different api response formats into account is just not possible for us - much bigger teams with much more money have failed at that. It is specifically not a design goal.
Got it. Let me know if I need to create a new issue for this: So scenarios like this are not possible, right?
Scenario:
What if another hook (hookB) wants to just read the same cache that is populated via hookA, but hookB does not have access to the expensive arg
value?
function hookA(props) {
const {data, isLoading} = useGetScrollingNoteQuery(expensiveArgThatIsOnlyAvailbleHere)
}
function hookB(props) {
const {data, isLoading} = useGetScrollingNoteQuery()
}
Or should I use something like this?
function hookB(props) {
const {data, isLoading} = scrollingNoteApi.endpoints.getScrollingNote.useQueryState(skipToken)
}
At that point, you should probably store the arg
in a Redux slice and make it available to both components that way
I am using RTK Query to fetch a list of items:
With this I can see that these actions are automatically dispatched:
comments/fetchComments/pending
comments/fetchComments/fulfilled
So everything working perfectly.
In this scenario, the
comments
piece of state is completely handled by RTK Query.But how can I manually dispatch some actions and change the cached data? i.e. How can I do CRUD operation on this piece of state (comments) without doing any API call through RTK Query? e.g. by dispatching:
Correct me if I am wrong, but I think when we use RTK Query, that piece of state that is handled by RTK Query is completely locked and we cannot interact with it from outside RTK Query mechanism. Is it true?
I know that this may be against the basic mindset behind RTK Query, but in some scenarios, these manual interactions on the cached data are needed.
I should mention that I can easily achieve my goal using normal redux-toolkit without RTK Query, but I would lose all the cool functionalities of RTK Query. i.e. I want to use RTK Query but also have the freedom to control the store via dispatching normal actions.