🙆‍♀️

【NextJs14】NextJs14 と 便利なライブラリ【#31Create Todo Server Actions】

2024/02/13に公開

【#31Create Todo Server Actions】

YouTube: https://youtu.be/iVyydLPOfIQ

https://youtu.be/iVyydLPOfIQ

今回はTodoを作成する関数を作成します。
こちらはNextJsのServerActionsを使用します。

まずは、下記ファイルの
isCompletedのデフォルトの値を設定します。

prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider  = "postgresql"
  url  	    = env("DATABASE_URL")
  directUrl = env("DIRECT_URL")
}

model Todo {
  id String @id @default(cuid())
  title String
  isCompleted Boolean @default(false)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
npx prisma generate
npx prisma db push

もしうまくいかない場合は

npx prisma migrate reset

を先に実行してデータベースを
初期化してみてください。
actions/todo-create
 "use server";
import * as z from "zod";
import { CreateTodoSchema } from "@/types/schema";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
import { auth } from "@clerk/nextjs";

export const CreateTodo = async (values: z.infer<typeof CreateTodoSchema>) => {
  const { userId } = auth();

  if (!userId) {
    return {
      error: "Unauthorized",
    };
  }

  const validatedFields = CreateTodoSchema.safeParse(values);

  if (!validatedFields.success) {
    return {
      error: "Invalid fields",
    };
  }

  const { title } = validatedFields.data;

  const todo = await db.todo.create({
    data: {
      title,
    },
  });

  console.log(todo);

  revalidatePath("/account");

  return { success: "New todo created!" };
};
app/(main)/account/_components/todo-form.tsx
"use client";
import { useTransition } from "react";
import * as z from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

import { CreateTodo } from "@/actions/todo-create";
import { CreateTodoSchema } from "@/types/schema";

import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { toast } from "sonner";

export const TodoForm = () => {
  const [isPending, startTransition] = useTransition();
  const form = useForm<z.infer<typeof CreateTodoSchema>>({
    resolver: zodResolver(CreateTodoSchema),
    defaultValues: {
      title: "",
    },
  });

  const onSubmit = (values: z.infer<typeof CreateTodoSchema>) => {
    startTransition(() => {
      CreateTodo(values)
        .then((data) => {
          toast.success(data.success);
        })
        .catch((data) => {
          if (data) {
            toast.error(data.error);
          } else {
            toast.error("Something went wrong!");
          }
        });
    });
  };

  return (
    <Form {...form}>
      <form
        onSubmit={form.handleSubmit(onSubmit)}
        className="flex items-center gap-x-1 w-full max-w-xl"
      >
        <FormField
          control={form.control}
          name="title"
          render={({ field }) => (
            <FormItem className="w-full">
              <FormControl>
                <Input
                  {...field}
                  disabled={isPending}
                  type="text"
                  placeholder="Enter your todo"
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit" disabled={isPending} variant="primary">
          Create
        </Button>
      </form>
    </Form>
  );
};

Discussion