react-rails で Rails の Controller 側のデータを React Component でレンダリングするサンプル

Rails のサーバーサイドでやりくりしているデータを React でレンダリングする方法を調べたメモです。

こちらの記事の続きです。
<a href="https://qiita.com/gotchane/items/6027cc07d757e64bd8bc" target="_blank" rel="nofollow noopener">react-rails と webpacker を使って Rails アプリケーション上で React を動かす</a>


## 前提

### 検証環境

* Rails 5.1.6
* ruby 2.4.3
* Mac OS X 10.13.4
* rails new したアプリケーションに下記 Gem がインストールされていること
  * <a href="https://github.com/reactjs/react-rails" target="_blank" rel="nofollow noopener">react-rails</a> 
  * <a href="https://github.com/rails/webpacker" target="_blank" rel="nofollow noopener">webpacker</a>

* <a href="https://qiita.com/gotchane/items/6027cc07d757e64bd8bc" target="_blank" rel="nofollow noopener">react-rails と webpacker を使って Rails アプリケーション上で React を動かす</a>の手順を追えていること

## 実現までのざっくりした流れ

大きく分けて以下2ステップで作成します。

1. Rails の Contoller のデータを JSON 返却する API を作成
2. API のレスポンスを React の Component で表示する

## 手順

### 1. Rails の Contoller のデータを JSON 返却する API を作成

#### API 用 Controller 作成

通常の Controller と namespace を分けて API 用の Controller を作成します。

```--no-assets --no-helper``` オプションで余計な assets や helper の生成を抑制しつつ作成します。

```
$ rails g controller api/v1/welcomes --no-assets --no-helper
```

#### Controller のアクションを定義

JSON で返却したいサンプルデータをハッシュで定義します。

```app/controllers/api/v1/welcomes_controller.rb
class Api::V1::WelcomesController < ApplicationController
  def index
    @data = { greeting: "HelloFromAPI" }
  end
end
```

#### view 作成

jbuilder を使い、 JSON を返却する view を定義します。

```app/api/v1/welcomes/index.json.jbuilder
json.extract! @data, :greeting
```

#### routes 作成

API にアクセスするための route を追記します。

```config/routes/rb
namespace :api, format: 'json' do
  namespace :v1  do
    resources :welcomes
  end
end
```

#### JSONの返却確認

http://localhost:3000/api/v1/welcomes にアクセスし、下記のようなデータが表示されればOKです。

```
{"greeting":"HelloFromAPI"}
```

### 2. API のレスポンスを React の Component で表示する

#### 表示したい React の Component 指定

JSON で返却できるようにしたデータを React の Component で表示します。
下記のように、表示したい Component と、JSON データを呼び出す API の URL を props で指定します。

```app/views/welcomes/index.html.erb
<%= react_component("HelloWorld", url: "/api/v1/welcomes") %>
```

#### React の Component 編集

<a href="https://reactjs.org/docs/faq-ajax.html" target="_blank" rel="nofollow noopener">React の公式ドキュメント</a>を参考に、React の Component を下記のように修正します。ざっくり下記のような処理を追加しています。

* ```constructor``` で state を定義する
* ```componentDidMount()``` 内で、```this.props.url``` で指定したAPIを ajax で呼び出し、結果を state にセット
* ```render()``` 内で state の内容を表示

```javascript:app/javascript/components/HelloWorld.js
import React from "react"
import PropTypes from "prop-types"
class HelloWorld extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      greeting: ''
    };
  }

  componentDidMount() {
    fetch(this.props.url)
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            greeting: result.greeting
          });
        },
        // Note: it's important to handle errors here
        // instead of a catch() block so that we don't swallow
        // exceptions from actual bugs in components.
        (error) => {
          this.setState({
            error
          });
        }
      )
  }

  render () {
    const { error, greeting } = this.state;
    return (
      <React.Fragment>
        Greeting: {greeting}
      </React.Fragment>
    );
  }
}

HelloWorld.propTypes = {
  greeting: PropTypes.string
};
export default HelloWorld
```

なお、React で ajax を使うときは、```componentDidMount``` でコールし、```setState``` で Component を更新出来るようにすべきと書いてありました。<a href="https://reactjs.org/docs/faq-ajax.html" target="_blank" rel="nofollow noopener">React の公式ドキュメント</a>の文章を以下に引用します。

> **Where in the component lifecycle should I make an AJAX call?**
> You should populate data with AJAX calls in the componentDidMount lifecycle method. This is so you can use setState to update your component when the data is retrieved.

#### 画面表示確認

```bin/webpack-dev-server``` と ```rails s``` を実行した状態で、 http://localhost:3000 にアクセスし、下記のような画面が表示されればOKです。 

![image.png](https://qiita-image-store.s3.amazonaws.com/0/222030/fbd136a3-97ac-56a9-de5b-33fe72d06869.png)

## 終わりに

今回は Rails の Contorller 側の データを ajax を使って React の Component で扱うやり方をさらいました。関連して Rails の API っぽい使い方や jbuilder にも触れてよかったです。

## 参考URL

* <a href="https://qiita.com/joe-re/items/96f12dda4a62470d1d7c" target="_blank" rel="nofollow noopener">react-railsを使ってReactのTutorialをやってみる</a>
* <a href="https://github.com/reactjs/react-rails#server-side-rendering" target="_blank" rel="nofollow noopener">GitHub - reactjs/react-rails: Integrate React.js with Rails views and controllers, the asset pipeline, or webpacker.</a>
* <a href="https://qiita.com/ryouzi/items/06cb0d4aa7b6527b3645" target="_blank" rel="nofollow noopener">Railsのjbuilderの書き方と便利なイディオムやメソッド</a>