react-railsでモーダルを実装する

[react-rails](https://github.com/reactjs/react-rails)でのモーダルの実装の方法をまとめます。
npmでReactのモーダル系ライブラリをインストールすればさくっと実装できるのですが、今回npmが使えないRails環境だったので、Gemを使った方法と自前実装する方法について見ていきます。

## Gemを使って簡単に実装する場合
[react_rails_modal](https://github.com/kiyodori/react_rails_modal)を使います。

### インストール
まずreact-railsとreact_rails_modalをGemfileに追加します。

```ruby:Gemfile
gem 'react-rails'
gem 'react_rails_modal'
```

インストールします。

```
$ bundle install
```

インストールコマンドを実行します。

```
$ rails g react:install
$ rails g react_rails_modal:install
```

これにより`application.js`に下記が追記されます。

```ruby:app/assets/javascripts/application.js
//= require react
//= require react_ujs
//= require components
//= require react_rails_modal
```

これでマニフェストファイルにライブラリの読み込みが宣言されました。

### 初期設定

続いて、react-railsの環境変数を設定します。

```ruby
# config/environments/development.rb
MyApp::Application.configure do
  config.react.variant = :development
end

# config/environments/production.rb
MyApp::Application.configure do
  config.react.variant = :production
end
```

### 使い方

jsxファイルの中でModalComponentコンポーネントを宣言して使います。

```js
<ModalComponent
  isOpen={bool}
  onCloseRequest={requestCloseFunction}
  style={customStyle}
>
  <h1>Modal Component Content</h1>
  <p>Text</p>
</ModalComponent>
```

### 実装例

具体的な使い方は下記のようになります。

`app/controller/views/`以下のテンプレートファイルで、Reactコンポーネントを呼び出します。
今回はHelloコンポーネントを呼び出しています。

```erb
<%= react_component('Hello') %>
```

Helloコンポーネントはjsxファイルの中で定義します。
その中でModalComponentを呼び出しており、ここがモーダル部分になります。

モーダルの開閉状態をstateで持って、クリックに応じてモーダルの開閉状態を切り替える関数を用意しています。
スタイルに関してはオーバーレイとモーダル本体それぞれを指定することができます。

```js
class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.closeModal = this.closeModal.bind(this);
    this.openModal = this.openModal.bind(this);
    this.state = { isModalOpen: false };
  }

  closeModal() {
    this.setState({ isModalOpen: false });
  }

  openModal() {
    this.setState({ isModalOpen: true });
  }

  render () {
    return (
      <div>
        <button onClick={this.openModal}>Open Modal</button>
        <ModalComponent
          isOpen={this.state.isModalOpen}
          onCloseRequest={this.closeModal}
          style={Hello.styles}
        >
          <h1>Hello world!</h1>
          <button onClick={this.closeModal}>Close</button>
        </ModalComponent>
      </div>
    );
  }
}

Hello.styles = {
  overlay: {
    backgroundColor: 'rgba(0,0,0,0.75)',
    bottom: '0',
    height: '100%',
    position: 'fixed',
    top: '0',
    left: '0',
    right: '0',
    width: '100%',
    zIndex: '1',
  },
  modal: {
    background: '#fff',
    left: '5%',
    padding: '15px 30px',
    position: 'relative',
    top: '10%',
    width: '80%',
    zIndex: '2',
  }
}
```

これでモーダル完成です。

## 自前実装する場合
ここからは自前実装する方法について見ていきます。

`app/controller/views/`以下のテンプレートファイルで、Reactコンポーネントを呼び出します。

```erb
<%= react_component('Hoge') %>
```

Hogeコンポーネントの中でModalコンポーネントを呼び出します。
親であるHogeコンポーネントで、モーダルの開閉状態のstateおよびその関数は定義します。

```js:hoge.es6.jsx
class Hoge extends React.Component {
  constructor(props) {
    super(props);
    this.closeModal = this.closeModal.bind(this);
    this.openModal = this.openModal.bind(this);
    this.state = { isModalOpen: false };
  }

  closeModal() {
    this.setState({ isModalOpen: false });
  }

  openModal() {
    this.setState({ isModalOpen: true });
  }

  render() {
    return (
      <div>
        <button onClick={this.openModal}>Open Modal</button>
        <Modal isOpen={this.state.isModalOpen} onClose={this.closeModal} />
      </div>
    );
  }
}
```

Modalコンポーネントの中では、引数として渡された`this.props.isOpen`が`false`の場合、`null`を返しています。
これによりモーダルを非表示にできます。

```js:modal.es6.jsx
class Modal extends React.Component {
  render() {
    if (!this.props.isOpen) {
      return null;
    }

    return (
      <div>
        <div onClick={this.props.onClose} style={Modal.styles.overlay} />
        <div style={Modal.styles.content}>
          <h1>Hello modal!</h1>
          <button onClick={this.props.onClose}>Close</button>
        </div>
      </div>
    );
  }
}

Modal.styles = {
  overlay: {
    backgroundColor: 'rgba(0,0,0,0.75)',
    bottom: '0',
    height: '100%',
    position: 'fixed',
    top: '0',
    left: '0',
    right: '0',
    width: '100%',
    zIndex: '1',
  },
  content: {
    background: '#fff',
    left: '5%',
    padding: '15px 30px',
    position: 'relative',
    top: '10%',
    width: '80%',
    zIndex: '2',
  }
}
```

以上でモーダルを自前実装できました。
親クラスでモーダルの開閉状態を管理することと、モーダルが閉じているときはrenderで`null`を返すようにすればモーダルの実装が簡単になると思います。