😸
【Convex】NextJs14 と Convex【#20 Convex mutation hooks】
【#20 Convex mutation hooks】
YouTube: 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