😸

【Convex】NextJs14 と Convex【#20 Convex mutation hooks】

2024/05/03に公開

【#20 Convex mutation hooks】

YouTube: https://youtu.be/C8S4A_KYJtI

https://youtu.be/C8S4A_KYJtI

今回はカード作成の関数の中身を実装します。

convexのusemutationは
「create」「delete」「update」で
同じ使い方となりますので、hooksにまとめます。

hooks/use-api-mutation.ts
import { useState } from "react";
import { useMutation } from "convex/react";

export const useApiMutation = (mutationFn: any) => {
  const [pending, setPeinding] = useState(false);
  const apiMutation = useMutation(mutationFn);

  const mutate = (payload: any) => {
    setPeinding(true);

    return apiMutation(payload)
      .then((result) => {
        return result;
      })
      .catch((error) => {
        throw error;
      })
      .finally(() => {
        setPeinding(false);
      });
  };

  return { mutate, pending };
};

次に以下のファイルの「onSubmit」の関数の中で実行します。

components/modals/create-card-modal.tsx
"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";

import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Form, FormControl, FormField } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Button } from "@/components/ui/button";

import { useCreateCardModal } from "@/store/use-create-card-modals";
import { useApiMutation } from "@/hooks/use-api-mutation";
import { api } from "@/convex/_generated/api";

const formSchema = z.object({
  title: z
    .string()
    .min(2, { message: "Title must be at least 2 characters." })
    .max(50, { message: "Title must be less than 50 characters." }),
  cardType: z.string({
    required_error: "Card type is required.",
    invalid_type_error: "Card type is required.",
  }),
});

export const CreateCardModal = () => {
  const { pending, mutate } = useApiMutation(api.card.create);
  const { isOpen, onClose, initialValues } = useCreateCardModal();

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      title: "",
      cardType: "comment",
    },
  });

  const onSubmit = (values: z.infer<typeof formSchema>) => {
    mutate({
      orgId: initialValues.id,
      title: values.title,
      cardType: values.cardType,
    })
      .then(() => {
        console.log("Card created");
        onClose();
      })
      .catch((error) => {
        console.log(error);
      });
  };

  return (
    <Dialog open={true} onOpenChange={onClose}>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Create Card</DialogTitle>
          <DialogDescription>
            Select card type & Enter card title
          </DialogDescription>
        </DialogHeader>

        <Form {...form}>
          <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
            <FormField
              control={form.control}
              name="cardType"
              render={({ field }) => (
                <Select
                  onValueChange={field.onChange}
                  defaultValue={field.value}
                >
                  <FormControl>
                    <SelectTrigger>
                      <SelectValue placeholder="Card type" />
                    </SelectTrigger>
                  </FormControl>
                  <SelectContent>
                    <SelectItem value="comment">Comment</SelectItem>
                    <SelectItem value="board">Board</SelectItem>
                  </SelectContent>
                </Select>
              )}
            />
            <FormField
              control={form.control}
              name="title"
              render={({ field }) => (
                <Input {...field} placeholder="Card title" />
              )}
            />

            <DialogFooter>
              <DialogClose asChild>
                <Button type="button" variant="outline">
                  Cancel
                </Button>
              </DialogClose>
              <Button type="submit">Create</Button>
            </DialogFooter>
          </form>
        </Form>
      </DialogContent>
    </Dialog>
  );
};

この状態でテストはできるのですが、
モーダルを開くタイミングで「orgId」を「isOpen」の引数として渡していますので、
今回のように強制的にモーダルを表示している場合、
convexのデータを確認した際には「orgId」は「""」と空の状態になっています。

Discussion