In my project I'd like to increase and decrease amount of the foods listed . but after push the "More" button in the first request doesn't change anything in database but after push it again in second request just increse it once (after this refresh the page you'll figure it out). actually I don't know why the first request doesn't counted .
Demo :https://laughing-jang-201dc1.netlify.app/ .
Github : https://github.com/alibidjandy/burger
this is the main RTK Query configuration :
https://github.com/alibidjandy/burger/blob/main/src/Api/apiSlice.ts
import { current } from "@reduxjs/toolkit";
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import {
AllBurgerType,
BurgerType,
IngredientsType,
} from "../components/Layouts/burger/burgerSlice/burgerSlice";
export const burgerApi = createApi({
reducerPath: "burgerApi",
tagTypes: ["Ingredients"],
baseQuery: fetchBaseQuery({
baseUrl:
"https://burger-order-brown-default-rtdb.europe-west1.firebasedatabase.app",
}),
endpoints: (builder) => ({
getIngredients: builder.query<BurgerType, undefined>({
// query: () => `/.json`,
query: () => {
return { url: "/.json", method: "GET" };
},
providesTags: ["Ingredients"],
}),
editIngredients: builder.mutation({
query: (initialIngredients) => {
return { url: "/.json", method: "PATCH", body: initialIngredients };
},
invalidatesTags: ["Ingredients"],
}),
increase: builder.mutation({
query: ({ ing }) => {
return {
url: `/ingredients/${ing.id}/.json`,
method: "PATCH",
body: ing,
};
},
async onQueryStarted({ ing }, { dispatch, queryFulfilled }) {
const patchResult = dispatch(
burgerApi.util.updateQueryData(
"getIngredients",
undefined,
(draft) => {
console.log("increase");
// debugger;
const ingredient = draft.ingredients.find(
(ingredient) => ingredient.title === ing.title
);
if (ingredient) {
console.log(current(ingredient));
ingredient.Qty!++;
}
}
)
);
try {
await queryFulfilled;
} catch {
console.log("crashed");
patchResult.undo();
}
},
}),
decrease: builder.mutation({
query: ({ ing, indexIng }) => {
return {
url: `/ingredients/${indexIng}/.json`,
method: "POST",
body: ing,
};
},
async onQueryStarted({ ing, indexIng }, { dispatch, queryFulfilled }) {
const patchResult = dispatch(
burgerApi.util.updateQueryData(
"getIngredients",
undefined,
(draft) => {
console.log("decrese");
const ingredient = draft.ingredients[indexIng];
if (ingredient.Qty !== undefined && ingredient.Qty > 0) {
draft.totalCost = +(draft.totalCost -=
ingredient.cost!).toFixed(2);
ingredient.Qty--;
}
}
)
);
try {
await queryFulfilled;
} catch {
patchResult.undo();
}
},
}),
}),
});
export const { editIngredients, getIngredients } = burgerApi.endpoints;
export const {
useGetIngredientsQuery,
useEditIngredientsMutation,
useIncreaseMutation,
useDecreaseMutation,
} = burgerApi;
Wait... I'm just re-reading your code. I think you might have a very wrong idea on what onQueryStarted
and updateQueryData
do. onQueryStarted
runs after your request has been sent to the server. updateQueryData
only updates your data on the client.
So you send the old client-side data to the server and then update it on the client side.
Yes, no wonder your data is always one step behind.
You need to send updated data to the server in the first place.
You would need to do something like
increase: builder.mutation({
query: ({ ing }) => {
return {
url: `/ingredients/${ing.id}/.json`,
method: "PATCH",
body: { ...ing, Qty: ing.Qty + 1 }
};
},
and no onQueryStarted
at all.
Just use invalidatesTags
to have the correct data refetched from the server afterwards.
Also, as I stumbled over that somewhere else in your code: you are generating an id
using nanoid()
and using that as key
. Never do that. A key
has to be stable, not be a different one on every render. That will always cause React to destroy the whole child DOM on every render and throw away the whole local state of all child components as well.