[RailsAPI x React]環境構築する方法

#作成経緯
RailsAPIモード使い方をまとめたかったので作成してます。備忘録なので所々違うかもしれません。
もっといい方法があればぜひ参考にしたいと考えています。今回はローカルで動くようにしていますので
本番環境では動きません。

#RaislAPI作成
```
$ rails new アプリ名 --api
```
```ruby:Gemfile
gem 'rack-cors'
#コメントアウトを外してbundle installする
```
```ruby:config/initializers/cors.rb

#コメントアウトを外す
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:3001'  #変更します。
    #origins 'example.com' #'http://localhost:3001'許可する

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```





#モデル作成
```
$ rails g migration モデル名
$ bundle exec rails db:migrate
```
普段と変わらないのでいつも通り作成してください。

#ルーティング作成
```ruby:config/routes.rb
Rails.application.routes.draw do
#----------ここから----------------
 namespace :api do
   namespace :v1 do
     resources :todos, only: %i[index show create update destroy]
   end
 end
#----------ここまで追加-------------
end
```
バージョン管理する時に`namespace`を使うと便利なので分けています。

#コントローラ作成
```ruby:app/controllers/api/v1/todos_controller.rb
class Api::V1::TodosController < ApplicationController
  def index
    todos = Todo.order(updated_at: :desc)
    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
    else
      render json: todo.errors, status: 422
    end
  end

  def update
    todo = Todo.find(params[:id])
    if todo.update(todo_params)
      render json: todo
    else
      render json: todo.errors, status: 422
    end
  end

  def destroy
    if Todo.destroy(params[:id])
      head :no_content
    else
      render json: { error: "Failed to destroy" }, status: 422
    end
  end

  private

  def todo_params
    params.require(:todo).permit(:name, :is_completed)
  end
end
```
今回は`JSON形式で`データを返すだけです。
`destroy`は返すデータがないため`head`を使っています。
[headについてはRailsガイド確認](https://railsguides.jp/layouts_and_rendering.html)

#controllerの書き方Part2
```ruby:app/controllers/api/v1/todos_controller.rb
module Api
  module V1
    class TodosController < ApplicationController
      def index
        todos = Todo.all

        render json: {
          todos: todos
        }, status: :ok
      end
    end
  end
end
```
`module ディレクトリ`〜`end`囲んでも`namespace`使うことが出来ます。

#React作成
アプリの直下でReactのアプリ作成する。

```
$ npx create-react-app front
```
```
$ cd front
$ npm install react-router-dom
$ npm install axios
$ npm install --save styled-components
$ npm install @material-ui/core @material-ui/icons @material-ui/lab
$ npm start
```
Reactの初期画面が出ていることを確認します。

```
$ mkdir src/urls
$ touch src/urls/index.js
```

```react:src/urls/index.js
const DEFAULT_API_LOCALHOST = 'http://localhost:3000/api/v1'

export const todosIndex = `${DEFAULT_API_LOCALHOST}/todos`
```
`axios`で通信するものを定数で定義します。

```react:src/apis/todos.js
import axios from 'axios';
import { todosIndex } from '../urls/index'

export const fetchTodos =() => {
  return axios.get(todosIndex)
  .then(res => {
    return res.data //通信成功
  })
  .catch((e) => console.error(e)) //通信失敗
}
```

axios.getでHTTPリクエストをしています。
通信に成功した場合は.then()なり、通信に失敗・例外の場合は.catch()になります。
作成した関数`fetchTodos()`呼び出したら`'http://localhost:3000/api/v1/todos`を呼び出し、JSON形式でデータが返って来ます。

```react
.then(res => {
    return res.data //通信成功
  })
```
`res`の中にデータが入り、`return`で`res.data`データだけ返しています。

```react
import React, { Fragment, useEffect } from 'react';
import { fetchTodos } from '../apis/todos';

export const Todos = () => {

  useEffect(() => {
    fetchTodos()
    .then((data) =>
      console.log(data)
    )
  }, [])

  return (
    <Fragment>
      Todo一覧
    </Fragment>
  )
}
```
`rails`側で`rails s`をし、`localhost:3000`立ち上げ、
`front`側で`npm start`します。`localhost:3000`は`rails`で使用しているため`localhost:3001`で立ち上げます。
これで環境構築は完成していると思います。
毎回、`rails`と`react`を両方立ち上げるのがめんどくさいので
今後の目標はDockerで環境構築したいと考えています。