👋
【NextJs14】NextJs14 と 便利なライブラリ【#33Update Todo Server Actions】
【#33Update Todo Server Actions】
YouTube: https://youtu.be/1bp8neXOO9E
今回はTodoのアップデートボタンを実装します。
動画の1:20秒当たりでスキーマのidの中身をコピーするのですが、
コピーではなくコードの移動をしていたので、
実際に作成する際にはコピーをお願いします。
app/(main)/account/_components/todo-actions.tsx
"use client";
import { useTransition } from "react";
import { toast } from "sonner";
import { CheckCircle, Trash2 } from "lucide-react";
import { deleteTodo } from "@/actions/todo-delete";
import { updateTodo } from "@/actions/todo-update";
import { Button } from "@/components/ui/button";
interface TodoActionsProps {
todoId: string;
isCompleted: boolean;
}
export const TodoActions = ({ todoId, isCompleted }: TodoActionsProps) => {
const [isPending, startTransition] = useTransition();
const onUpdate = () => {
startTransition(() => {
updateTodo({ id: todoId, isCompleted: isCompleted })
.then((data) => toast.success(data.success))
.catch((data) => {
if (data.error) {
toast.error(data.error);
} else {
toast.error("Something went wrong!");
}
});
});
};
const onDelete = () => {
startTransition(() => {
deleteTodo({ id: todoId })
.then((data) => toast.success(data.success))
.catch((data) => {
if (data.error) {
toast.error(data.error);
} else {
toast.error("Something went wrong!");
}
});
});
};
return (
<div className="flex items-center gap-x-2">
<Button
onClick={onUpdate}
disabled={isPending}
variant={isCompleted ? "Completed" : "secondary"}
>
<CheckCircle className="h-5 w-5" />
</Button>
<Button onClick={onDelete} disabled={isPending} variant="destructive">
<Trash2 className="h-5 w-5" />
</Button>
</div>
);
};
types/schema.ts
import * as z from "zod";
export const CreateTodoSchema = z.object({
title: z.string().min(1, {
message: "Title is required.",
}),
});
export const DeleteTodoSchema = z.object({
id: z.string({
required_error: "Id is required",
invalid_type_error: "Id is required",
}),
});
export const UpdateTodoSchema = z.object({
id: z.string({
required_error: "Id is required",
invalid_type_error: "Id is required",
}),
isCompleted: z.boolean({
required_error: "IsCompleted is required",
invalid_type_error: "IsCompleted is required",
}),
});
actions/todo-update.ts
"use server";
import * as z from "zod";
import { UpdateTodoSchema } from "@/types/schema";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
import { auth } from "@clerk/nextjs";
export const updateTodo = async (values: z.infer<typeof UpdateTodoSchema>) => {
const { userId } = auth();
if (!userId) {
return {
error: "Unauthorized",
};
}
const validatedFields = UpdateTodoSchema.safeParse(values);
if (!validatedFields.success) {
return {
error: "Invalid fields",
};
}
const { id, isCompleted } = validatedFields.data;
await db.todo.update({
where: {
id,
},
data: {
isCompleted: !isCompleted,
},
});
revalidatePath("/account");
return { success: "Todo updated!" };
};
Discussion