0
0

Next.js×RailsでEdit画面を作る

Last updated at Posted at 2024-08-26

https://qiita.com/gabakugik/items/b0fcc09158d336e9e2a8
これの続きです。

ツリー構造

|-- app
|   |-- favicon.ico
|   |-- globals.css
|   |-- layout.tsx
|   |-- page.tsx
|   `-- todos
|-- components
|   |-- CreateTodoForm.tsx
|   |-- DeleteTodoButton.tsx
|   |-- EditTodoForm.tsx
|   |-- Todo.tsx
|   `-- Todos.tsx

todos以下のフォルダ構成

-- [id]
    |-- edit
    |   `-- page.tsx
    `-- page.tsx
/backend/app/controllers/todos_controller.rb
class TodosController < ApplicationController
    
    def index
        # 日付が新しい順に10件まで取得する
        @todos = Todo.all.order(created_at: :desc).limit(10)
    
        render json: @todos
    
    end
    
    def show
    
        @todo = Todo.find(params[:id])

    render json: @todo
    end

    def create
          
        @todo = Todo.new(todo_params)
      
        if @todo.save
            render json: @todo, status: :created, location: @todo
          else
            render json: @todo.errors, status: :unprocessable_entity
          end
        end
      
        def update
          @todo = Todo.find(params[:id])
      
          if @todo.update(todo_params)
            render json: @todo
          else
            render json: @todo.errors, status: :unprocessable_entity
          end
        end
        
          # DELETE /todos/:id
          def destroy
            @todo = Todo.find(params[:id])
            @todo.destroy
          
          end
        
        def todo_params
          params.require(:todo).permit(:title, :content)
    end
end
/frontend/components/EditTodoForm.tsx
"use client";

import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import axios from 'axios';

type EditTodoFormProps = {
  id: number;
};

// Todo を編集するフォーム
const EditTodoForm = ({ id }: EditTodoFormProps) => {
  const router = useRouter();

  // フォームの入力値を管理するstate
  const [title, setTitle] = useState('');

  // フォームの入力値を管理するstate
  const [content, setContent] = useState('');

  // idが変更されたら(=Todo編集ページを開いたら)、Todoを取得してフォームの初期値を設定する
  useEffect(() => {
    // idが存在しない場合は、処理を中断する
    const fetchTodo = async () => {
      try {
        // idを元にTodoを取得する
        const res = await axios.get(`http://localhost:3000/todos/${id}`);

        // フォームの初期値を設定する
        const { title, content } = res.data;
        setTitle(title);
        setContent(content);
      } catch (err) {
        console.log(err);
      }
    };

    // idが存在する場合は、Todoを取得する
    if (id) {
      fetchTodo();
    }
  }, [id]);

  // フォームの入力値を更新する関数
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      // APIを呼び出して、Todoを更新する
      await axios.put(`http://localhost:3000/todos/${id}`, { todo: { title, content } });

      // Todoの更新に成功したら、Todo詳細ページに遷移する
      router.push(`/todos/${id}`);
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <div className="space-y-6 py-16">
      <form
        onSubmit={handleSubmit}
        className="space-y-6"
      >
        <label className="block text-xl font-bold text-gray-700">Edit Todo</label>
        <input
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          placeholder="タイトル"
          className="block w-full py-2 pl-3 pr-4 text-base border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-gray-500 focus:border-gray-500 sm:text-sm"
        />
        <textarea
          value={content}
          onChange={(e) => setContent(e.target.value)}
          placeholder="本文"
          className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-gray-500 focus:border-gray-500 sm:text-sm"
        />
        <button
          type="submit"
          className="mt-3 ml-auto flex justify-center py-2 px-8 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
        >
          Update
        </button>
      </form>
    </div>
  );
};

export default EditTodoForm;
/frontend/app/todos/[id]/edit/page.tsx
"use client";

import { useParams } from 'next/navigation';
import Link from 'next/link';
import EditTodoForm from '@/components/EditTodoForm';

const EditTodoPage = () => {
  // Use useParams to retrieve the id from the URL
  const { id } = useParams<{ id: string }>();

  // Display loading state until id is available
  if (!id) {
    return <div>Loading...</div>;
  }

  return (
    <div className="flex justify-center items-center">
      <div className="flex flex-col w-3/4 max-w-lg">
        <EditTodoForm id={parseInt(id)} />
        <Link
          href="/"
          className="ml-auto font-medium text-blue-600 hover:bg-blue-300 focus:outline-none"
        >
          Back
        </Link>
      </div>
    </div>
  );
};

export default EditTodoPage;
/frontend/app/todos/[id]/page.tsx
"use client";

import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import useSWR from 'swr';
import Link from 'next/link';
import Todo from '@/components/Todo';
import { TodoType } from '@/types/Todo';
import axios from 'axios';
import DeleteTodoButton from '@/components/DeleteTodoButton';

// Fetcher function for SWR
const fetcher = (url: string) => axios.get(url).then(res => res.data);

// Todo詳細ページを表示するコンポーネント
const TodoDetail = ({ params }: { params: { id: string } }) => {
  // ルーティング情報を取得する
  const router = useRouter();
  const { id } = params;

  // SWRを使ってデータを取得する
  const { data: todo, error } = useSWR<TodoType>(id ? `http://localhost:3000/todos/${id}` : null, fetcher);

  // エラーが発生した場合の処理
  if (error) return <div>Failed to load</div>;

  // Todoを取得中の場合は「Loading...」を表示する
  if (!todo) return <div>Loading...</div>;

  return (
    <div className="flex justify-center items-center">
      <div className="flex flex-col space-y-6 w-3/4 max-w-lg pt-10">
        <label className="block text-xl font-bold text-gray-700">Todo</label>
        <Todo todo={todo} />
        <div className="flex justify-end">
          <Link
            href={`/todos/${id}/edit`}
            className="mt-auto font-medium text-blue-600 hover:bg-blue-300 focus:outline-none mr-12"
          >
            Edit
          </Link>
          <Link
            href="/"
            className="mt-auto font-medium text-blue-600 hover:bg-blue-300 focus:outline-none"
          >
            Back
          </Link>
        </div>
        {/* 削除ボタンコンポーネントを追加 */}
        <DeleteTodoButton id={todo.id} />
      </div>
    </div>
  );
};


export default TodoDetail;

これでEdit画面は作れたはずです。
最後に削除機能を作ります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0