Next.js×RailsTodo 作成機能の実装

Todo作成機能を作る

前回のhttps://qiita.com/gabakugik/items/bd013dcc92fd64564139
続き

```/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

        def todo_params
          params.require(:todo).permit(:title, :content)
    end
end
```

```/frontend/app/components/CreateTodoForm.tsx
"use client";

import { useState } from 'react';
import axios from 'axios';
import useSWR, { mutate } from 'swr';

// APIのエンドポイント
const API_URL = 'http://localhost:3000/todos';

// Fetcher関数
const fetcher = (url: string) => axios.get(url).then(res => res.data);

// Todoを作成するフォーム
const CreateTodoForm: React.FC = () => {
  // フォームの入力値を管理するstate
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');

  // SWRを使ってデータをフェッチ
  const { data, error } = useSWR(API_URL, fetcher);

  // フォームの入力値を更新する関数
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      // APIを呼び出して、Todoを作成する
      await axios.post(API_URL, {
        todo: {
          title,
          content,
        },
      });

      // Todoの作成に成功したら、フォームの入力値をリセットする
      setTitle('');
      setContent('');

      // Todoの作成に成功したら画面を更新する(Todoを再取得するため)
      mutate(API_URL);
    } catch (error) {
      console.error(error);
    }
  };

  if (error) return <div>Failed to load todos</div>;
  if (!data) return <div>Loading...</div>;

  return (
    <div className="space-y-6 w-3/4 max-w-lg py-10">
      <form onSubmit={handleSubmit} className="space-y-6">
        <label className="block text-xl font-bold text-gray-700">New 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"
        >
          Create
        </button>
      </form>
    </div>
  );
};

export default CreateTodoForm;


```

```/frontend/app/page.tsx

import Todos from '@/components/Todos';
import CreateTodoForm from '@/components/CreateTodoForm';
// トップページ
export default function Home() {
  return (
    <div className="flex flex-col justify-center items-center">
      <Todos />
      <CreateTodoForm />
    </div>
  );
}

```

これで完成。
localhost:3001でフォームがあり、追加できる。