I have a route where i fetch all categories
getAllCategoriesAdmin: builder.query({
query: () => 'categories/allAdmin',
providesTags: ['CategoriesAdmin']
})
Then I get categories data from data refactored part of useGetAllCategoriesAdminQuery
const { isLoading, isError, data = {} } = useGetAllCategoriesAdminQuery();
Now I want to implement search field where I want to update that data part with new data in other words when I create new route
getAllCategoriesAdminSearch: builder.query({
query: () => 'categories/allAdminSearch',
providesTags: ['CategoriesAdmin'],
})
Is there any way that I can reupdate data part from useGetAllCategoriesAdminQuery so I dont have to make another data from useGetAllCategoriesAdminSearchQuery
const { data = {} } = useGetAllCategoriesAdminSearchQuery();
I don't get your question completely but I'm assuming you have a query API and you need the search query for this API. I hope this helps you.
there are many ways to re-fetch the data:
1:
I think your API should support some query params for searches like this:
getAllCategoriesAdmin: builder.query({
query: (searchInput) => `categories/allAdmin/?search=${searchInput}`,
providesTags: ['CategoriesAdmin']
})
RTK query creates a cache key base on query URL and params so if you change the search input it will automatically create a new request and fetch the data.
2:
another way is using invalidateTags for example if you have a mutaion query you can invalidate the tags in this case CategoriesAdmin and that causes the RTK clear the cache for corresponding query and refech the data.
3:
refetch function. every useHookQuery in RTK has a re-fetch function that you can assign it to your search event for re-fetching the new data.
const { refetch, isLoading, isError, data = {} } = useGetAllCategoriesAdminQuery();
Related
I have a list component that uses react-query to fetch the list data (in this case tickets)
<List
fetch={{
route: "tickets",
queryKey: "tickets"
}}
...
/>
The component fetches the data like so
const { isLoading, isError, data, refetch } = useQuery([queryKey], () => {
return fetchEntity({ route: `${route}${window.location.search}` })
})
fetchEntity does nothing else than using axios to fetch an return the data
I'm also providing window.location.search to the route because I have a filter component that sets the filter to the url.
The filter works, but when I refetch (by refocusing the window), there are 2 different requests in the network tab.
One with the filter params and the other one without the filter params. Therefore, the list shows the unfiltered data for a few milliseconds and then the filtered data again.
I also tried to use the query like so
const { isLoading, isError, data, refetch } = useQuery([queryKey, window.location.search], () => {
return fetchEntity({ route: `${route}${window.location.search}` })
})
This fixed the problem where the unfiltered data was shown for a few milliseconds. However, the old data is still fetched (maybe it is just faster now so not visible in the ui?).
Can anyone explain this behaviour of react query?
The problem was I was fetching with the same query key in a child component. That's why I had multiple requests.
I'm using cache.writeQuery to update my cache after a mutation, and it just is not updating the cache. The updates just aren't in there.
This is in my update() callback from the useMutation:
const userData = copy(cache.readQuery({
query: GET_USER
}))
userData.me.categories.push(newCategory)
cache.writeQuery({
query: GET_USER,
data: userData
})
console.log('New category shows up here:')
console.log(userData.me.categories.map(category => category.id))
const _userData = copy(cache.readQuery({
query: GET_USER,
}))
console.log('New category is not in here:')
console.log(_userData.me.categories.map(category => category.id))
I'm deep-cloning my user data, and simply pushing onto its categories. It just doesn't want to store the updated data. It's clearly doing something, insofar as I can mutate all my existing categories and change their names etc, it just will not add a new one.
What can I try next?
I'm trying to extract selectors from queries in my apiSlice as said in this documentation: https://redux.js.org/tutorials/essentials/part-8-rtk-query-advanced
The documentation put this example:
const usersAdapter = createEntityAdapter()
const initialState = usersAdapter.getInitialState()
export const extendedApiSlice = apiSlice.injectEndpoints({
endpoints: builder => ({
getUsers: builder.query({
query: () => '/users',
transformResponse: responseData => {
return usersAdapter.setAll(initialState, responseData)
}
})
})
})
export const { useGetUsersQuery } = extendedApiSlice
// Calling `someEndpoint.select(someArg)` generates a new selector that will return
// the query result object for a query with those parameters.
// To generate a selector for a specific query argument, call `select(theQueryArg)`.
**// In this case, the users query has no params, so we don't pass anything to select()**
export const selectUsersResult = extendedApiSlice.endpoints.getUsers.select()
const selectUsersData = createSelector(
selectUsersResult,
usersResult => usersResult.data
)
export const { selectAll: selectAllUsers, selectById: selectUserById } =
usersAdapter.getSelectors(state => selectUsersData(state) ?? initialState)
Now, i need to have selectById selector (that by default takes userid as second parameter).
I can't manage to have a working selectById selector when my query looks like this
endpoints: builder => ({
getUsers: builder.query({
query: (applicationId) => `/application/${applicationId}/users`,
transformResponse: responseData => {
return usersAdapter.setAll(initialState, responseData)
}
})
})
How do I extract selectById selector from adapter and how i use it in a component with useSelector when I have this kind of query with arguments?
Thanks anyone that will help me
I feel like you mismatch concepts a bit, so it leads to confusion.
First of all, let's clarify, that's selectors you a using- it's not a selector to your state in usersAdapter, but to RTK-Q's own state.
By handling a response in transformResponse you are just copying the data from it to your's, usersAdapter's state.
So, considering that, you should be specific about which state you are going to select from. If you want it from the "final destination", i.e. from the adaptor's state, it's should be done via:
const usersSelectors = **usersAdapter**.getSelectors(
(state) => state.users // or something like that
)
Otherwise, using the selectors from apiSlices like extendedApiSlice from your example - you are fetching the data from RTK-Q's cached state, which may not contain some old data after the cache invalidation. If it's still your goal, the limitation is that RTK-Q's store isn't a normalized store you may expect, with ids and values, but rather the key-value pairs, where keys are your requests, and the values - last results (users arrays in your case). So, if you have no API endpoint defined for selecting a particular user by ID, you won't be able to select it from RTK-Q's state directly. But you may select the cached users by applicationId, and find your user by id in the result array. I bet it's not what you actually want, so most probably you need just to prepare selectors for your own store, as I've mentioned above.
RTKQ I'm creating a mutation. I'm trying to update it with the optimistic update method, but in the examples I've found, the scenario is always like this. postEdit mutation works. getPost request is made in onQueryStarted and it is finalized. In my scenario, instead of sending a getPost request, postEdit returns the current post value as a response if the statusCode is positive. In this case, I couldn't find how to update the store, do you have any ideas?
That's not the right way to use RTK Query or any other fetch library that handles revalidation for you like useSWR. You want to retrieve data from a query and perform revalidation after mutations on that data so the query gets automatically re-executed after each mutation and your UI is updated.
Let me give you this example I used in one of my applications:
addPost: builder.mutation({
query: (body) => {
console.log('ADDING POST', body)
return {
url: `add-post`,
method: 'POST',
body,
}
},
invalidatesTags: ['Posts'],
onQueryStarted(body, { dispatch, queryFulfilled }) {
const patchResult = dispatch(
socialApi.util.updateQueryData(
'getPosts',
{ _id: body.profileOwner_id, limit: body.limit },
(draft) => {
console.log('POSTS ARRAY DRAFT', draft)
draft.unshift({
...body,
shareable: true,
title: 'published a new post.',
timestamp: new Date(),
likes: [],
})
}
)
)
queryFulfilled.catch(patchResult.undo)
},
}),
In this case I invalidate 'Posts' which triggers the query to User's Posts after the user adds a new Post. No need to add the new post manually in response to the mutation.
And as you can see, this way I can use the optimistic update util method .updateQueryData(), "draft" is the Posts list, I just add a new Post object to that array and that is automatically rendered as soon as the mutation is triggered.
I use React with Redux and Firebase. Here is one of the functions from my Action.js
export const loadItemsInCategory = (categoryId) => {
return (dispatch) => {
let itemsArray = [];
firestoreService.getItemsInCategory(categoryId)
.then(updatedGroceryList => {
itemsArray = updatedGroceryList;
console.log(`category id is ${categoryId}`)
dispatch(loadItemsInCategoryHelper(categoryId, itemsArray))
})
.catch((error) => console.log(error));
}
}
It's a normal FireStore query. Here is what happens in firestoreService.getItemsInCategory(categoryId)
export const getItemsInCategory = async (categoryId) => {
console.log(`firebase category id is ${categoryId}`)
const snapshot = await db.collection('Item').where('category', '==', categoryId).get()
return snapshot.docs.map(doc => {console.log("called");return {id: doc.id, ...doc.data()}});
}
Right now, my application shows the list of items in the given Category. However, the list does not get updated when a new Item is added to the category by someone else. In other words, additions in FireStore collection does not reflect on my screen unless I refresh the page.
How can I code my webapp in such a way that any change on the FireStore end gets reflected on my webapp?
Thanks in advance!
Your code is doing a one-time query with get(). Queries made like this are not realtime. They don't refresh.
If you want to receive updates to your query in realtime, you should follow the documentation for realtime queries. Instead of using get(), you will use onSnapshot(). And instead of getting a promise, you will attach a listener callback that will be invoked whenever there is a change in the results of the query. Because of these differences, your code will look drastically different.