Skip to content

Instantly share code, notes, and snippets.

@kuc-arc-f
Created November 28, 2024 09:11
Show Gist options
  • Save kuc-arc-f/3d47ef11cb8d595a352396d0dd3f7e24 to your computer and use it in GitHub Desktop.
Save kuc-arc-f/3d47ef11cb8d595a352396d0dd3f7e24 to your computer and use it in GitHub Desktop.
react-router-7, CRUD example
import type { Route } from "./+types/home";
import { Form , useSubmit } from "react-router";
import { useState , useEffect } from "react";
import { z } from "zod";
export function meta({}: Route.MetaArgs) {
return [
{ title: "New React Router App" },
{ name: "description", content: "Welcome to React Router!" },
];
}
// スキーマの定義
const todoSchema = z.object({
title: z.string().min(1, "タイトルは必須です"),
description: z.string().optional(),
completed: z.boolean().optional(),
});
type Todo = z.infer<typeof todoSchema> & {
id: number;
created_at: string;
updated_at: string;
};
export async function loader({ params }: Route.LoaderArgs) {
let apiUrlBase = import.meta.env.VITE_WORKERS_URL;
console.log(params);
//console.log("apiUrlBase=", apiUrlBase);
let apiUrl = apiUrlBase + "/api/todo4";
console.log("apiUrl=", apiUrl);
const response = await fetch(apiUrl);
const data = await response.json();
console.log(data);
return { todos: data};
}
export async function action({
request,
}: Route.ActionArgs) {
let formData = await request.formData();
const action = formData.get("_action");
let title = await formData.get("title");
console.log("action=", action);
const input = {
title: title
}
console.log(input);
let apiUrlBase = import.meta.env.VITE_WORKERS_URL;
switch (action) {
case "create":
case "update": {
const title = formData.get("title") as string;
//const description = formData.get("description") as string;
const description = "";
const completed = false;
const id = formData.get("id") as string;
try {
const validatedData = todoSchema.parse({ title, description, completed });
console.log("apiUrlBase=", apiUrlBase);
console.log(validatedData);
const response = await fetch(
`${apiUrlBase}/api/todo4${action === "update" ? `/${id}` : ""}`,
{
method: action === "create" ? "POST" : "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(validatedData),
}
);
return { success: true };
} catch (error) {
if (error instanceof z.ZodError) {
//return json({ errors: error.errors }, { status: 400 });
return { errors: error.errors };
}
//return json({ error: "Failed to save todo" }, { status: 500 });
return json({ error: "Failed to save todo" }, { status: 500 });
}
}
case "delete": {
const id = formData.get("id");
console.log("delete.id=", id);
const response = await fetch(`${apiUrlBase}/api/todo4/${id}`, {
method: "DELETE",
});
return { success: true };
}
default:
return json({ error: "Invalid action" }, { status: 400 });
}
return {data : input };
}
import DialogForm from './test2/DialogForm';
const DIALOG_FORM_CREATE = "dialog_form_create";
const DIALOG_FORM_EDIT = "dialog_form_edit";
//
export default function Product({
loaderData,
actionData,
}: Route.ComponentProps) {
let submit = useSubmit();
const todos = loaderData.todos;
const [formData, setFormData] = useState<Todo>(null);
console.log(todos);
//console.log(actionData);
useEffect(() => {
if(actionData){
console.log(actionData);
if(actionData.success){
console.log("#success");
location.reload();
}
}
}, [actionData]);
const handleDelete = async function(todo){
console.log(todo);
if (!window.confirm("Delete OK?")) {
return;
}
if(todo) {
const formData = new FormData();
formData.append("_action", "delete");
formData.append("id", todo.id);
submit(formData, { method: "post" });
}
}
const handleEdit = async function(todo){
console.log(todo);
setFormData(todo);
if(todo) {
openEdit();
}
}
const openCreate = function(){
const modalDialog = document.getElementById(DIALOG_FORM_CREATE);
if(modalDialog) { modalDialog.showModal(); }
}
const closeCreate = function(){
const modalDialog = document.getElementById(DIALOG_FORM_CREATE);
if(modalDialog) { modalDialog.close(); }
}
const openEdit = function(){
const modalDialog = document.getElementById(DIALOG_FORM_EDIT);
if(modalDialog) { modalDialog.showModal(); }
}
const closeEdit = function(){
const modalDialog = document.getElementById(DIALOG_FORM_EDIT);
if(modalDialog) { modalDialog.close(); }
}
return (
<div className="container mx-auto p-4">
<h1 className="text-3xl font-bold">Test2</h1>
<hr />
<button onClick={()=>openCreate()}>[ Add ]</button>
<dialog id={DIALOG_FORM_CREATE} className="dialog">
<div className="bg-white px-8 pt-3 pb-3 dialog_body_wrap">
<p className="text-3xl font-bold">Add </p>
<hr className="my-3" />
<DialogForm idName={DIALOG_FORM_CREATE} mode="create" formData={null} />
</div>
</dialog>
{/* edit_Form */}
<dialog id={DIALOG_FORM_EDIT} className="dialog">
<div className="bg-white px-8 pt-3 pb-3 dialog_body_wrap">
<p className="text-3xl font-bold">edit </p>
<hr className="my-3" />
<DialogForm idName={DIALOG_FORM_EDIT} mode="edit"
formData={formData} />
</div>
</dialog>
{/* List */}
<div className="space-y-4">
{todos.map((todo) => (
<div
key={todo.id}
className="flex items-center justify-between p-4 border rounded-lg"
>
<div>
<h3 className="font-semibold">{todo.title}</h3>
<p className="text-sm text-gray-600">{todo.description}</p>
</div>
<div className="flex gap-2">
<button
variant="outline"
onClick={() => handleEdit(todo)}
>
編集
</button>
<button
variant="destructive"
onClick={() => handleDelete(todo)}
>
削除
</button>
</div>
</div>
))}
</div>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment