react-railsを使って、簡単なデータ表示＋検索のサンプルを作ってみる

# はじめに
仕事でRailsを使用して開発をしているのですが、事情によりReactを導入することになりました。
しかし、「Reactって何？」状態の自分にとって、いきなりReactを導入と言われても...
なので、事前にReactを勉強するため、色々参考にしながら、簡単なサンプルを作ってみました。

## 使用した技術とバージョン
ruby 2.5.1
Rails 5.2.2
react-rails 2.4.7
WebPacker 3.5.5

## 参考にした記事や書籍

Reactの学習には、こちらの書籍を使用しました。
今回記述したサンプルコードも、こちらの内容を大いに参考にさせて頂きつつ、記述していきました。

吉田裕美 著「作りながら学ぶ React入門」(2017)
https://www.amazon.co.jp/%E4%BD%9C%E3%82%8A%E3%81%AA%E3%81%8C%E3%82%89%E5%AD%A6%E3%81%B6-React%E5%85%A5%E9%96%80-%E5%90%89%E7%94%B0%E8%A3%95%E7%BE%8E/dp/479805075X/ref=sr_1_1?ie=UTF8&qid=1545646648&sr=8-1&keywords=%E4%BD%9C%E3%82%8A%E3%81%AA%E3%81%8C%E3%82%89%E5%AD%A6%E3%81%B6react%E5%85%A5%E9%96%80

また、絞り込み部分はこちらの記事を参考にさせて頂きました。
「[Reactで絞り込み機能付き検索を実装してみた](https://qiita.com/yoshimo123/items/fa21b4cdd293b57b8cc9)」

こちら2つについて、参考にさせて頂きありがとうございました。

## 成果物

<img width="604" alt="タスクリスト.png" src="https://qiita-image-store.s3.amazonaws.com/0/284056/af509803-5222-fdfb-3b19-54bbcc1ac2aa.png">

内容としてはシンプルで、タスク管理のページを作りました。
一応タスクの名前で検索できるようになってます。
（ちなみにタスクの登録は未実装...時間ができたら作ります。）

#### 実装方法は？
上の画面は、全てReactで描画するようになっています。
よって、**html自体には、Reactのコンポーネントを呼び出す処理**しか書いてないです。

## 実装の流れ

### 1、前準備

[react-railsのgithub](https://github.com/reactjs/react-rails)に載っている方法でプロジェクトを作ったりしつつ、
**Get started with Webpacker**
まで進めます。
そこから、

```linux
rails g controller schedule
rails g model Task name:string description:text start_date:string end_date:string
rails db:migrate
```

で必要なコントローラやモデルを作り、DBとテーブルも作成します。
面倒ですが、タスク登録がないので、insert文でタスクを頑張って入れます...
（start_dateやend_dateがstringなのは、日付の処理が面倒だったからです...）

### 2、rails側の整備
コントローラ、ビュー、ルーティングに実装を追加し、rails側の準備を完了させます。
views/scheduleにindex.html.erbを追加し、以下のように実装を加えます。

```ruby:index.html.erb
 <%= react_component("Task", { tasks: @tasks }) %>
```

またコントローラ、ルーティングは以下のようにします。

```ruby:schedule_controller.rb
 class ScheduleController < ApplicationController
    def index
        @tasks = Task.all();
    end
end
```

```ruby:routes.rb
Rails.application.routes.draw do
  get '/schedule', to: 'schedule#index', as: 'schedule'
end
```

Rails側でやっていることは、
①タスクを表示するためのページを作る
②コントローラで、DBからTaskを全て取得し、ビューに渡してあげる
③ビューでは、コントローラから渡ってきたTaskを、Reactコンポーネントに渡す
だけです。

### ３、Reactの実装
いよいよReactのソースを実装します。
app/javascript/components配下に、
Task.js
を追加します。
もしくは、

```linux
rails g react:component Task
```

でも作成できるようです。

今回実装した、Task.jsの中身は以下の通りです。
一応部分部分にコメントを入れて解説していますが、
間違ってたらごめんなさい...　

```react:Task.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'

//メインのコンポーネント
class Task extends Component {
  //コンストラクタ。
  //このコンポーネントのstateには絞り込み前のタスク一覧と、絞り込み後のタスク一覧をそれぞれ用意する
  constructor(props){
    super(props)
    this.state = {initialTasks: this.props.tasks, tasks:[]}
  }

  //ブラウザロード時の処理。
  //最初はタスク全部を表示しておく
  componentDidMount() { 
    this.setState({tasks: this.state.initialTasks})
  }

  //検索のメソッドをここで用意しておく
  searchByName(name) { 
    const result = this.state.initialTasks.filter((task) => {
      return task.name.toLowerCase().search( name.toLowerCase()) !== -1;
    })
    this.setState({tasks: result})
  }

  /* 
    ページ全体のrenderメソッド。
    大事なのは、FilterFormのprops（search）に、上記で定義したsearchByNameを定義しておくこと。
    これにより、Taskコンポーネントのstateにあるtasksを変更することができる。
    そして、変更したtasksを、TaskListコンポーネントにpropsで渡してあげることで、
    絞り込み後のタスク一覧を表示することができる。
  */
  render() {
    return (
      <div>
        <h1>タスクリスト</h1>
        <FilterForm search={(name) => this.searchByName(name)} />
        <TaskList tasks={this.state.tasks} />
      </div>
    )
  }
}

//検索フォームのコンポーネント
class FilterForm extends Component{

  //コンストラクタ。ここでは、検索値nameをstateとして持っておく
  constructor(props){
    super(props)
    this.state = {name: ''}
  }

  //検索のテキストボックスの中身が変更された時の処理。
  //stateに検索値を挿入しておく
  onChangeName(event) {
    this.setState({name : event.target.value})
  }

  //検索ボタンをクリックされた時の処理。
  //上記で書いた通り、 Taskのコンポーネントで渡されたsearchメソッドを実行することにより、
  //Taskコンポーネントのstateに、絞り込み後のタスク一覧を表示することができる
  onClickSearch() {
    this.props.search(this.state.name)
  }

  //検索フォームのrenderメソッド。
  render() {
    return (
      <div className="entry">
        <fieldset>
          <legend>検索</legend>
          <div>名前で検索: <input type="text" value={this.state.name} name="name" onChange={(e) => this.onChangeName(e)} placeholder="例：買い物" /> </div>
          <div> <input type="submit" value="検索" onClick={(e) => this.onClickSearch(e)} /> </div>
        </fieldset>
      </div>
    )
  }
}

//タスク一覧についてのコンポーネント。
//こちらは上2つのクラスコンポーネントとは違い、関数コンポーネントになっている。
//クラスコンポーネントとは違い、できることが限られているため、シンプルな表示だけしたいときに使う。
const TaskList = (props) => { 
  //タスク一覧を表示する。
  //繰り返し処理にはmap関数を使用。
  return (
    <div>
      <table className="task">
        <thead  data-type="ok">
          <tr><th>名前</th><th>詳細</th><th>開始日</th><th>終了日</th></tr>
        </thead>
        <tbody>
          {props.tasks.map((task) =>
            <TaskItem task={task} key={task.id} /> )}
         </tbody>
      </table>
    </div>
  )
}
//TaskListコンポーネントが受け取るpropsを定義。
//ここではタスク一覧を受け取ることができるように定義しておく。
TaskList.propTypes = {
  tasks: PropTypes.array.isRequired
}

//タスクの1つの行を表すコンポーネント。
//上と同様関数コンポーネント。
const TaskItem = (props) => {
  //受け取ったタスクのオブジェクトの値を、それぞれ行のセルに挿入。
  const {name, description, start_date, end_date} = props.task
  return (
    <tr>
      <td>{name}</td>
      <td>{description}</td>
      <td>{start_date}</td>
      <td>{end_date}</td>
    </tr>
  )
}
//TaskItemコンポーネントが受け取るpropsを定義。
//ここではタスクオブジェクト。
TaskItem.propTypes = {
  task: PropTypes.object.isRequired
}

//react-railsではこの行がないとエラーになるっぽい。
//メインとなるクラス名（ここではTask）、ここで書いているクラス名、ファイル名の3つの名前が一緒じゃないと、
//エラーが起こって実行できなかった。
export default Task
```

Rails側から渡ってきたタスク一覧を、メインのTaskコンポーネントに渡すところから始まってます。
最初にこのページに遷移したときは、
①渡されたtasksを、TaskコンポーネントのstateのinitialTasksに入れて、それをそのまま同じstateのtasksに挿入する。
②state.tasksをTaskListコンポーネントに渡して、ループを回してTaskモデルの内容を抜き取って表示する
という感じの処理をしているかと思います。

一方、何かしらの検索がなされた時は、
①FilterFormにあらかじめ渡してあるsearchByNameメソッドを使って、入力値による絞り込みを行う（これにより、Taskコンポーネントのstate.tasksを更新することができる）。
②更新されたstate.tasksがTaskListコンポーネントに渡される。あとは上記と同様、ループを回してTaskモデルの内容を表示する
ということをしています。

実装できたら、

```linux
rails s
```

でサーバーを起動し、/scheduleにアクセスします。
タスク一覧が表示でき、絞り込みができたら成功！

# 最後に

Reactが全くわからない状態から、サンプルの力を借りてここまで来れました。
まだ触ったばかりなので内容としては全然かと思いますが、
これからReact＋Railsを始める方にとって一助となれば幸いです。

ここまで読んで頂き、ありがとうございました！