fix: only tanstack usage in optimistic updates

This commit is contained in:
2025-08-29 23:39:51 +04:00
parent 8b06d08664
commit 568bd4ad36
19 changed files with 218 additions and 198 deletions

View File

@ -1,5 +1,9 @@
import React from "react";
import { useMutation, UseMutationOptions } from "@tanstack/react-query";
import {
useMutation,
UseMutationOptions,
useQueryClient,
} from "@tanstack/react-query";
import { AxiosError } from "axios";
import { Text } from "@mantine/core";
import { modals } from "@mantine/modals";
@ -20,9 +24,8 @@ type CrudOperations<TEntity, TUpdate> = {
};
type UseEntityOperationsProps<TEntity extends BaseEntity, TUpdate, TCreate> = {
entities: TEntity[];
setEntities: React.Dispatch<React.SetStateAction<TEntity[]>>;
refetch: () => void;
key: string;
queryKey: any[];
mutations: {
create: UseMutationOptions<
any,
@ -50,9 +53,8 @@ const useCrudOperations = <
TUpdate extends object,
TCreate extends object,
>({
entities,
setEntities,
refetch,
key,
queryKey,
mutations,
getCreateEntity,
getUpdateEntity,
@ -61,30 +63,81 @@ const useCrudOperations = <
TEntity,
TUpdate
> => {
const onError = (error: AxiosError<HttpValidationError>) => {
const queryClient = useQueryClient();
const onError = (
error: AxiosError<HttpValidationError>,
_: any,
context: any
) => {
console.error(error);
notifications.error({
message: error.response?.data?.detail as string | undefined,
});
refetch();
if (context?.previous) {
queryClient.setQueryData(queryKey, context.previous);
}
};
const onSettled = () => queryClient.invalidateQueries({ queryKey });
const createMutation = useMutation({
...mutations.create,
onError,
onSuccess: (res: { entity: TEntity }) => {
setEntities([...entities, res.entity]);
},
onSettled,
});
const updateMutation = useMutation({
...mutations.update,
onError,
onSettled,
onMutate: async ({ body: { entity: update } }) => {
await queryClient.cancelQueries({ queryKey: [key] });
const previous = queryClient.getQueryData(queryKey);
queryClient.setQueryData(queryKey, (old: { items: TEntity[] }) => {
let updated = old.items.map((entity: TEntity) =>
entity.id === update.id
? getUpdateEntity(entity, update)
: entity
);
if ("lexorank" in update) {
updated = sortByLexorank(
updated as (TEntity & { lexorank: string })[]
);
}
return {
...old,
items: updated,
};
});
return { previous };
},
});
const deleteMutation = useMutation({
...mutations.delete,
onError,
onSettled,
onMutate: async ({ path: { pk } }) => {
await queryClient.cancelQueries({ queryKey: [key] });
const previous = queryClient.getQueryData(queryKey);
queryClient.setQueryData(queryKey, (old: { items: TEntity[] }) => {
const filtered = old.items.filter(e => e.id !== pk);
return {
...old,
items: filtered,
};
});
return { previous };
},
});
const onCreate = (name: string) => {
@ -99,7 +152,7 @@ const useCrudOperations = <
});
};
const onUpdate = (id: number, update: TUpdate) => {
const onUpdate = async (id: number, update: TUpdate) => {
updateMutation.mutate({
body: {
entity: update,
@ -107,17 +160,6 @@ const useCrudOperations = <
path: { pk: id },
query: undefined,
});
setEntities(prev => {
const updated = prev.map(entity =>
entity.id === id ? getUpdateEntity(entity, update) : entity
);
if ("lexorank" in update) {
return sortByLexorank(
updated as (TEntity & { lexorank: string })[]
);
}
return updated;
});
};
const onDelete = (entity: TEntity, onSuccess?: () => void) => {
@ -131,7 +173,6 @@ const useCrudOperations = <
onConfirm: () => {
deleteMutation.mutate({ path: { pk: entity.id } } as any);
onSuccess && onSuccess();
setEntities(prev => prev.filter(e => e.id !== entity.id));
},
});
};