Why is updateQueryData from RTK query not updating my state? - reactjs

I'm using RTK query and I want to overwrite the state with the result from my transform request. I get my overview of a todos array by calling the getTodosOverview Query. After that I call a updateTodos query and this gives me back a new array with todos. I want to overwrite the original array with these results. I'm using the function updateQueryData but it doesnt seem to work. What am I missing here?
export const todosApi = createApi({
reducerPath: 'todosApi',
baseQuery: fetchBaseQuery({ baseUrl: 'api/todos' }),
endpoints: (builder) => ({
getToDosOverview: builder.query<Array<ToDos>, string>({
query: () => `getOverview`,
transformResponse: (rawResult: { data: Array<ToDos> }) => rawResult.data,
keepUnusedDataFor: 0,
}),
updateTodos: builder.mutation<Array<ToDos>, string>({
query: (fileId) => ({
url: `updateTodos?fileId=${fileId}`,
method: 'POST',
}),
transformResponse: (rawResult: { data: Array<ToDos> }) => rawResult.data,
async onQueryStarted(uniqueIdentifier, { dispatch, queryFulfilled }) {
const { data }= await queryFulfilled;
// Update state with new data from response
const patchResult = dispatch(
todosApi.util.updateQueryData(
'getToDosOverview',
uniqueIdentifier,
() => {
return data;
}
)
);
},
}),
}),
});

Well, you're not returning anything new.
const patchResult = dispatch(
todosApi.util.updateQueryData(
'getToDosOverview',
uniqueIdentifier,
// here you are getting the old state as a variable called `ToDos`
(ToDos: Array<ToDos>) => {
// and here you return that old state without any change
return ToDos;
}
)
);
Also, you are doing that way before you have even received a response.
The response will be available after the line
await queryFulfilled;
so you probably want to do something like
const { data } = await queryFulfilled
and then use data as the new value.
Generally, it seems like you are copy-pasting from the "optimistic updates" example. Please look at the example of pessimistic updates instead.
Also, with your code you are using the wrong argument if you want to update useGetToDosOverviewQuery().
You are updating useGetToDosOverviewQuery(uniqueIdentifier) here.
You should probably call
todosApi.util.updateQueryData('getToDosOverview', undefined, ...)

Related

RTK query not refreshing - why?

I have a RTK query that's not refreshing it's core content after a delete mutation. Could anyone explain why ? It's not clear to me where the problem lies as there is not refresh request made at any point.
The code looks fine and it's pretty much the same I use in another API that's working. And on this second API I pass the same data (a list of items) and it's refreshing fine after a delete ; here's the code:
:
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react';
import { Auth, API, Storage } from 'aws-amplify';
// Define a service using a base URL and expected endpoints
export const researchApi = createApi({
reducerPath: 'researchApi',
tagTypes: ['Research'],
baseQuery: fetchBaseQuery({
baseUrl: process.env.NEXT_PUBLIC_API_RESEARCH,
prepareHeaders: async (headers, { getState }) => {
const token = (await Auth.currentSession()).getIdToken().getJwtToken();
headers.set('Authorization', `${token}`);
headers.set('Content-Type', 'application/json');
return headers;
}
}),
endpoints: (builder) => ({
getResearch: builder.query({
query: () => `research`,
providesTags: ['Research']
}),
getResults: builder.query({
query: (id) => `results?searchid=${id}`,
}),
addResearch: builder.mutation({
query(keywords) {
const data = {
keywords: keywords
}
return {
url: `research`,
method: 'POST',
body: data
}
},
invalidatesTags: ['Research']
}),
deleteResults: builder.mutation({
query(results) {
// send array
let sanitized;
sanitized = results.filter(item => item);
const data = {
items: sanitized
}
//console.log('data: ', data);
return {
url: `removeresult`,
method: 'DELETE',
body: data
}
},
invalidatesTags: ['Research']
}),
}),
})
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetResearchQuery, useGetResultsQuery, useAddResearchMutation, useDeleteResultsMutation } = researchApi
I'm calling the query like this :
const router = useRouter()
const { kwd } = router.query
const { data, error, isError, isLoading } = useGetResultsQuery(kwd);
if(isLoading) {
return (
<>
<Spinner animation="border" size="sm" role="status" />{' '} Please wait while Loading...
</>
)
}
Any idea would be very helpful as I'm completely lost with this...
Ok so problem solved, I didn't add the correct parameters :
getResearch: builder.query({
query: () => research,
providesTags: ['Research']
}),
getResults: builder.query({
query: (id) => `results?searchid=${id}`,
providesTags: ['Research'] // ========> THAT WAS MISSING
}),
Try and also make sure you are returning the correct data from the mutation. Thanks.
Return the correct fields from the mutation. If the required the field, can be id is not returned from the mutation, then there will be no refresh.

Redux RTK Query - Lazy Query

I simply want to use RTK to run an API request as part of an onSuccess function and await the response. I was doing this in Axios no problem but I'm trying to replace my Axios calls with RTK and having trouble using the LazyQuery.
I'm getting an exception
useLazyQuery is not a function or its return value is not iterable
My API:
const linkAPI = overweightRTK.injectEndpoints({
tagTypes: ['Link'],
endpoints: (builder) => ({
createLink: builder.mutation({
query: (body) => ({
url: `/links/`,
method: 'POST',
body,
invalidatesTags: ['Link']
})
})
}),
overrideExisting: false
});
export const { useCreateLinkMutation} = linkAPI;
My component:
const [trigger, result, lastPromiseInfo] = OverweightRTK.endpoints.createLink.useLazyQuery()
const onSuccess = async (data) => {
const createLinkRequest = {
payload: data
};
trigger({ createLinkRequest }, { skip: !isSubscriber })
.unwrap()
.then(res => {
if (res.status === 200) {
setSuccessMessage('Success, your account linked');
}
})
.catch((error) => console.log(error));
};
I figured it out. Apparently for mutation endpoints you don't need to use the lazyQuery(). Mutation queries by default return a similar response as lazyQuery and already return the tuple containing the trigger.
Working code: (This replaces the first line in my component code, nothing else needed to be changed)
const [trigger, result, lastPromiseInfo] = useCreateLinkMutation();
Useful docs on the subject: https://redux-toolkit.js.org/rtk-query/usage/mutations

RTK query based on data from another request

I have some rtk query, that query data based on response of another request ( with axios )
const { dat } = useGetDataQuery({
keys // it comes from another request
})
export const someApi = createApi({
reducerPath: 'someApi',
baseQuery,
endpoints: (builder) => ({
getData: builder.query<
any,
{ keys: string[] }
>({
query: (arg) => {
const { keys } = arg
return {
url: '/some_endpoint',
params: {
keys,
},
}
},
}),
}),
})
And in this case data from previous request it's keys.
Is it possible make request inside query and use this data for my query, instead of doing
this request outside and pass data as params?
I wouldn't recommend it. Theoretically it is possible, using queryFn, as you have access to getState there - but if that state value would ever change, that wouldn't re-execute your query. It's really best to just put in dynamic values as argument.

FetchbaseQuery invalidate cache

I have 2 completely independent components without any parent-child relationship being displayed on a single page.
Component 1 : Makes an API call fetches some records and display it in a table having server side-pagination
Component 2 : Contains a form, when the user submits the form the data in the component 1 needs to be refetch-ed through the backend.
Since I am using fetchBaseQuery to query the data, I believe I need to invalidate the cache in order to make the API call in the component 1.
I tried refetch() to fulfil that requirement but got no luck. I also tried setting the cache timeout using keepUnusedDataFor that too didn't work. Also, tried to do something with the tags, but for that I will have to use mutation instead of query and I am not sure how mutation is useful as per my use case
Here's some of the code :
component1.tsx
let { data, error, isSuccess, isError, isFetching, refetch } = useGetQuery(request, { skip});
const records = data?.records;
React.useEffect(() => {
if (records) {
// set records within table
}
}, [records]);
useGetQuery.ts
const extendedApi = mainApi.injectEndpoints({
endpoints: (builder) => ({
getQuery: builder.query<response, request>({
query: (request?: request) => ({
url: "someURL",
body: request,
method: "POST",
}),
providesTags: ["Requests"],
}),
}),
overrideExisting: true,
});
export const { useGetQuery } = extendedApi;
component2.tsx
let [trigger, data] = useSubmitFormMutation();
const submitForm = (e) => {
e.preventDefault();
trigger(// Some Object);
}
React.useEffect(() => {
if (isSuccess) {
updateRefreshRecords(true); // setting the hook to true to make an API call in component 1
}
}, [isSuccess]);
useSubmitFormMutation.ts
const extendedApi = mainApi.injectEndpoints({
endpoints: (builder) => ({
submitForm: builder.mutation<response, request>({
query: (request?: request) => ({
url: "some_other_url",
body: request,
method: "POST",
}),
invalidatesTags: ["Requests"],
}),
}),
overrideExisting: false,
});
export const { useSubmitFormMutation } = extendedApi;
mainAPI.ts
export const dynamicBaseQuery: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (args, api, extraOptions) => {
const { mainApiUrl } = (api.getState() as RootState).settings.endpoints;
const rawBaseQuery = fetchBaseQuery({
baseUrl: mainApiUrl,
prepareHeaders: (headers, { getState }) => {
// Use getState to pull the jwtToken and pass it in the headers to the api endpoint.
const { jwtToken } = (getState() as RootState).auth;
headers.set("authorization", jwtToken);
return headers;
},
});
return rawBaseQuery(args, api, extraOptions);
};
export const mainApi = createApi({
reducerPath: "mainApi",
baseQuery: dynamicBaseQuery,
endpoints: () => ({}),
tagTypes: ["Requests"],
});
store.ts
export const store = configureStore({
reducer: {
// other reducers
[localApi.reducerPath]: localApi.reducer,
[mainApi.reducerPath]: mainApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
})
.concat(localApi.middleware)
.concat(mainApi.middleware),
});
Can you please help me how can I invalidate the cache as per my use case.
Any help would be highly appreciated
Thanks
You can just add invalidatesTags to your mutation and that should refresh the query:
const extendedApi = mainApi.injectEndpoints({
endpoints: (builder) => ({
submitForm: builder.mutation<response, request>({
query: (request?: request) => ({
url: "some_other_url",
body: request,
method: "POST",
}),
invalidatesTags: ["Requests"]
}),
}),
overrideExisting: false,
});
No need for manual refetching or keepUnusedDataFor.
If that doesn't work, double-check that you added the api's middleware to the middlewares in your configureStore
Simply change your submitForm endpoint to mutation type and invalidate "Requests" tag on this endpoint. This way you don't have to use updateRefreshRecords.
You can then remove below useEffect in Component1.tsx
React.useEffect(() => {
if (refreshRecords) {
refetch();
}
}, [refreshRecords]);
and also remove keepUnusedDataFor: 5, from getQuery endpoint
I am not sure how mutation is useful as per my use case
When form is submitted, you are either creating or updating some data on backend. So, mutation is the right type of endpoint here. Use query type endpoint when you want to fetch some data from backend.

How to refresh page after deleting an item using REDUX/RTK Query?

I am fetching data from my api using RTK Query like this
export const coinApi = createApi({
reducerPath: 'coinApi',
baseQuery: fetchBaseQuery({ baseUrl }),
endpoints: (builder) => ({
getCoins: builder.query({
query: () => createRequest(`/watchlist`),
})
}),
});
and im deleting a coin from my table like this
export const deleteCoin = (id) => async (dispatch, getState) => {
try {
dispatch({
type: COIN_DELETE_REQUEST,
});
await axios.delete(`/api/coins/watchlist/${id}`);
dispatch({
type: COIN_DELETE_SUCCESS,
});
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({
type: COIN_DELETE_FAIL,
payload: message,
});
}
};
and in my frontEnd component: I am calling dispatch(deleteCoin(id));
the delete functionality is working, since in my database it is removed however the component does not refresh so the coin still exists on the UI unless I refresh the page myself manually.
I've tried accessing the global data from the RTK query, but cannot do it successfully
I was trying to use useEffect and pass in the dependency data from
const { data, isFetching } = useGetCoinsQuery();
However its still not reloading my component?
How else can i reload my component? This is my first time using RTK Query so I'm not sure how to really access that data and how can it listen to data changes in teh API server?
Thanks
const coins = useSelector((state) => state.coinApi.queries)
const {
loading: loadingDelete,
error: errorDelete,
success: successDelete,
} = coinDelete;
useEffect(() => {}, [dispatch, successDelete, data]);
if (isFetching) return <Loader></Loader>;
const deleteHandler = (id) => {
if (window.confirm('Are you sure?')) {
dispatch(deleteCoin(id));
}
};
Normally, you can use providesTags and invalidatedTags withing RTK-Query to make related queries automatically refetch after a mutation is run. In your case, your delete is not a mutation, but you can still use that mechanism.
In the long run I would encourage you to make a mutation out of your delete action here though, since RTK-Query will work a lot better the more you do in there - and you won't have to have all that code written by hand.
baseQuery: fetchBaseQuery({ baseUrl }),
tagTypes: ['Coins'],
endpoints: (builder) => ({
getCoins: builder.query({
query: () => createRequest(`/watchlist`),
providesTags: [ 'Coins' ]
})
await axios.delete(`/api/coins/watchlist/${id}`);
dispatch({
type: COIN_DELETE_SUCCESS,
});
dispatch(api.util.invalidateTags(['Coins'])) // this will refetch all queries that "provide" the tag `"Coins"`
} catch (error) {
You should read this example https://redux-toolkit.js.org/rtk-query/usage/examples. Notice that provideTags and invalidatesTags.
A general way to refetch:
const MyComponent = () =>{
const { refetch, data, error, isFetching} = useGetGithubByNameQuery();
const toRender = error ? (
<p>Oh no, there was an error</p>
) : isFetching ? (
<p>"Loading..."</p>
) : data ? (
data.map((item)=>{return <p>item.toString()</p>})
) : null;
return(
<>
{toRender}
<button onClick={refetch}>Refresh</button>
</>
)
}

Resources