[Laravel+React] Todoアプリ チートシート

[Laravel5.7でReactを利用した開発をしてみる](https://qiita.com/zaburo/items/fbcdd73d8d707357c25f)の記事をさらにコピペで実装できるように自分の備考録としてチートシートにまとめてみました。
初めてする方は上記記事に詳しく説明がまとめられておりますので、そちらを参照してください。

# 環境構築
[Laravel環境構築 チートシート](https://qiita.com/ProgramingDai/items/6fe5f678bf5a05e27c05)

# 手順

```
php artisan preset react
npm install && npm run dev
php artisan make:migration create_todos_table
```

```php:database/migrations/xxxxx_create_todos_table.php
		// 中略
		Schema::create('todos', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title')->nullable();
            $table->timestamps();
        });
        // 中略
```

```
php artisan migrate

php artisan make:model Todo

php artisan tinker
$todo1 = new App\Todo;
$todo1->title = 'Todo';
$todo1->save();
```

```php:routes/web.php
Route::get('/{any}', function(){
    return view('welcome');
})->where('any','.*');
```

```js:resouces/js/app.js
require('./components/TodoApp');
```

```php:resouces/views/welcome.blade.php
<!doctype html>
<html lang="ja">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- CSS -->
    <link rel="stylesheet" href="{{ asset('css/app.css') }}">

    <title>react test</title>
</head>

<body>
    <div id="app">
        <div class="container">
            <h3 class="mt-5">Todo 管理システム</h3>

            <!-- ここを置き換えていく -->
            <div id="todoApp"></div>

        </div>
    </div>

<!-- avaScript -->
<script src="{{ asset('js/app.js')}}"></script>
</body>
</html>
```

```php:routes/api.php
Route::group(['middleware' => 'api'], function(){
    Route::get('get', 'TodoController@getTodos');
    Route::post('add', 'TodoController@addTodo');
    Route::post('del', 'TodoController@deleteTodo');
});
```

```
php artisan make:controller TodoController
```

```php:app/Http/Controllers/TodoController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Todo;

class TodoController extends Controller
{
    //getTodos
    public function getTodos()
    {
        $todos = Todo::all();
        return $todos;
    }

    //add todo
    public function addTodo(Request $request)
    {
        $todo = new Todo;
        $todo->title = $request->title;
        $todo->save();

        $todos = Todo::all();
        return $todos;
    }

    //delete
    public function deleteTodo(Request $request)
    {
        $todo = Todo::find($request->id);
        $todo->delete();

        $todos = Todo::all();
        return $todos;
    }
}
```

```js:resources/js/components/TodoApp.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

//RenderRowsの機能実装
function RenderRows(props){
    //mapでループしている（for相当）
    return props.todos.map(todo => {
        return (
            <tr key={todo.id}>
                <td>{todo.id}</td>
                <td>{todo.title}</td>
                <td>
                    <button 
                        className="btn btn-secondary" 
                        onClick={() => props.deleteTask(todo)}
                    >
                        完了
                    </button>
                </td>
            </tr>
        );
    });
}

export default class TodoApp extends Component {

    //コンストラクタ内でstateにtodosを宣言
    constructor(){
        super();
        this.state = {
            todos: [],
            todo: ''
        };
        this.inputChange = this.inputChange.bind(this);
        this.addTodo = this.addTodo.bind(this);
        this.deleteTask = this.deleteTask.bind(this);
    }

    //コンポーネントがマウントされた時点で初期描画用のtodosをAPIから取得
    componentDidMount(){
        axios
            .get('/api/get')
            .then((res) => {
                //todosを更新（描画がかかる）
                this.setState({
                    todos: res.data
                });
            })
            .catch(error => {
                console.log(error)
            })
    }

    //入力がされたら（都度）
    inputChange(event){
        switch(event.target.name){
            case 'todo':
                this.setState({
                    todo: event.target.value
                });
                break;
           default:
                break;
       }
    }

    //登録ボタンがクリックされたら
    addTodo(){
        //空だと弾く
        if(this.state.todo == ''){
            return;
        }

        //入力値を投げる
        axios
            .post('/api/add', {
                title: this.state.todo
            })
            .then((res) => {
                //戻り値をtodosにセット
                this.setState({
                    todos: res.data,
                    todo: ''
                });
            })
            .catch(error => {
                console.log(error);
            });
    }

    //完了ボタンがクリックされたら
    deleteTask(todo){
        axios
            .post('/api/del', {
                id: todo.id
            })
           .then((res) => {
                this.setState({
                    todos: res.data
                });
            })
            .catch(error => {
                console.log(error);
            });
    }

    //テーブルの骨組みを描画し、行の描画はRenderRowsに任せる（その際、todosを渡す）
    render() {
        return (
            <React.Fragment>
                <div className="form-group mt-4">
                    <label htmlFor="todo">新規Todo</label>
                    <input 
                        type="text" 
                        className="form-control" 
                        name="todo" 
                        value={this.state.todo} 
                        onChange={this.inputChange}
                        placeholder="Todoを入力してください"
                    />
                </div>
                <button className="btn btn-primary" onClick={this.addTodo}>登録</button>
                <table className="table mt-5">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>タスク</th>
                            <th>完了</th>
                        </tr>
                    </thead>
                    <tbody>
                        {/* 行の描画 */}
                        <RenderRows
                            todos={this.state.todos}
                            deleteTask={this.deleteTask}
                        />
                    </tbody>
                </table>
            </React.Fragment>
        );
    }
}

ReactDOM.render(<TodoApp />, document.getElementById('todoApp'));
```