4.4 낙관적 업데이트 패턴 적용(작성/수정) o
This commit is contained in:
81
src/lib/mutations/posts.ts
Normal file
81
src/lib/mutations/posts.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
"use client";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { fetchJson } from "@/lib/api";
|
||||
import { queryKeys } from "@/lib/queryKeys";
|
||||
|
||||
type CreateCommentInput = {
|
||||
postId: string;
|
||||
authorId?: string;
|
||||
content: string;
|
||||
isAnonymous?: boolean;
|
||||
isSecret?: boolean;
|
||||
};
|
||||
|
||||
export function useCreateComment(postId: string) {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (input: CreateCommentInput) =>
|
||||
fetchJson<{ comment: any }>("/api/comments", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(input),
|
||||
}),
|
||||
onMutate: async (input) => {
|
||||
await qc.cancelQueries({ queryKey: queryKeys.comments.list(postId) });
|
||||
const previous = qc.getQueryData<any>(queryKeys.comments.list(postId));
|
||||
const optimistic = {
|
||||
id: `temp-${Date.now()}`,
|
||||
content: input.content,
|
||||
isAnonymous: !!input.isAnonymous,
|
||||
isSecret: !!input.isSecret,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
qc.setQueryData(queryKeys.comments.list(postId), (old: any) => ({
|
||||
comments: [optimistic, ...(old?.comments ?? [])],
|
||||
}));
|
||||
return { previous };
|
||||
},
|
||||
onError: (_err, _vars, ctx) => {
|
||||
if (ctx?.previous) qc.setQueryData(queryKeys.comments.list(postId), ctx.previous);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.comments.list(postId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useTogglePin(postId: string) {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (vars: { pinned: boolean; order?: number | null; userIdHeader?: string }) =>
|
||||
fetchJson<{ post: { id: string; isPinned: boolean; pinnedOrder: number | null } }>(
|
||||
`/api/posts/${postId}/pin`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: vars.userIdHeader ? { "x-user-id": vars.userIdHeader } : undefined,
|
||||
body: JSON.stringify({ pinned: vars.pinned, order: vars.order ?? null }),
|
||||
}
|
||||
),
|
||||
onMutate: async (vars) => {
|
||||
// Optimistic update detail cache
|
||||
await qc.cancelQueries({ queryKey: queryKeys.posts.detail(postId) });
|
||||
const previousDetail = qc.getQueryData<any>(queryKeys.posts.detail(postId));
|
||||
qc.setQueryData(queryKeys.posts.detail(postId), (old: any) => ({
|
||||
...(old ?? {}),
|
||||
post: {
|
||||
...(old?.post ?? { id: postId }),
|
||||
isPinned: vars.pinned,
|
||||
pinnedOrder: vars.pinned ? vars.order ?? 0 : null,
|
||||
},
|
||||
}));
|
||||
return { previousDetail };
|
||||
},
|
||||
onError: (_err, _vars, ctx) => {
|
||||
if (ctx?.previousDetail) qc.setQueryData(queryKeys.posts.detail(postId), ctx.previousDetail);
|
||||
},
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: queryKeys.posts.detail(postId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user