reduxを試してみた(2日目) - React.jsを学ぶ


React.jsと組み合わせるfluxのライブラリreduxを試してみた。 **このページは作業ログです。やったことをつらつら書くだけなのでまとめません。あしからず。**

## 対象読者

- 主に自分

## 今日のゴール

- Reactの構成を理解できていること
- Reactコンポーネントのイベントハンドラの設定が理解できていること

## やったことメモ

### Reactのドキュメントを読む

[チュートリアル(日本語)。](https://facebook.github.io/react/docs/tutorial-ja-JP.html)に目を通しつつ、手を動かす。

[チュートリアルの結果](https://github.com/kompiro/react-tutorial/)

#### 感想

- 手を動かしながら学べるのは良い
- `<script type="text/jsx">`の中に実装をしていく？まず、tutorial1.jsを実装してみる。
- コンポーネントの定義ができる下記のようなスニペットがあるとよさそう

```javascript
       var Component = React.createClass({
         render : function(){
           return (
             // write JSX
           );
         }
       });
```

- ES6だと下記のように記述できる

```javascript
import {Component} from 'react';

class Hoge extends Component {
  render() {
    return (
      // write JSX
    );
  }
}
```

- Componentに対するイベントハンドラは下記のように定義したクラスの中のfunctionで定義できる。`onSubmit={this.handleSubmit}`の辺りが該当コード。

```javascript
       var CommentForm = React.createClass({
         handleSubmit : function(e) {
         　　// ...
         },
         render : function() {
           return (
           <form className="commentForm" onSubmit={this.handleSubmit}>
             <input type="text" placeholder="Your Name..." ref="author"/>
             <input type="text" placeholder="Say somethings..." ref="text"/>
             <input type="submit" value="Post"/>
           </form>
           );
         }
       });
```

- ~~`this.props`ってもしかして親コンポーネントなのか？~~→違う自Componentのプロパティ。`<CommentForm onCommentSubmit={this.handleCommentSubmit}/>`の例だと、呼び出し元でイベントハンドラ`onCommentSubmit`が追加されている。

```javascript
      var CommentForm = React.createClass({
        handleSubmit : function(e) {
          e.preventDefault();
          var author = React.findDOMNode(this.refs.author).value.trim();
          var text = React.findDOMNode(this.refs.text).value.trim();
          if (!author || !text) {
            console.log('invalid value');
            return;
          }
          // TODO: submit data to server
          this.props.onCommentSubmit({author: author, text: text}); // onCommentSubmitはCommentFormに追加したイベントハンドラ
          React.findDOMNode(this.refs.author).value = '';
          React.findDOMNode(this.refs.text).value = '';
          return;
        },
```

### チュートリアル以外

#### Reusable Component

[ドキュメント](https://facebook.github.io/react/docs/reusable-components.html#es6-classes)

##### Classの定義

- ES6環境ではコンポーネントは下記のように継承を使って定義可能

```javascript
class HelloMessage extends React.Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}
```

##### propTypes

- コンポーネントにプロパティを設定した時にバリデーションする
- 開発時のみ有効になるらしい→プロダクション環境への切り替えはどうやるの？
- 型チェックやら必須チェックやら
- 新規に関数を作成したらカスタムバリデータとして機能

### Reduxのチュートリアルに戻る

#### 2.5 Reactとつなげる

[ドキュメント](http://rackt.github.io/redux/docs/basics/UsageWithReact.html)

`AddTodo `、`TodoList `、`Todo `、`Footer`どれもDumb Component。データがどこから来てどう変更するのか知らなくてもOKなので、Reduxに依存しなくても大丈夫。

ReactのコンポーネントとReduxを接続するには

1. `react-redux`を依存に追加
2. 下記の`index.js`のようにSmart Componentであるコンテナを`<Provider>`でラップする(Providerはreact-redux/Provider)
3. Appコンポーネントの定義`App.js`で、`react-redux/connect()`を使って接続する
 
```javascript:index.js
let rootElement = document.getElementById('root');
React.render(
  // The child must be wrapped in a function
  // to work around an issue in React 0.13.
  <Provider store={store}>
    {() => <App />}
  </Provider>,
  rootElement
);
```

```javascript:App.js
// Wrap the component to inject dispatch and state into it
export default connect(select)(App);
```

上記のconnectで`App.props`に`store.dispatch`が追加になり、`App.state`と`store.getState()`がバインドされる。つまり、reduxのstoreの変更が`App.state`の変更になる?

##### [react-reduxのリポジトリを見る](https://github.com/rackt/react-redux)

```javascript:container/CounterContainer.js
import { Component } from 'react';
import { connect } from 'react-redux';

import Counter from '../components/Counter';
import { increment } from '../actionsCreators';

// Which part of the Redux global state does our component want to receive as props?
function mapStateToProps(state) {
  return {
    value: state.counter
  };
}

// Which action creators does it want to receive by props?
function mapDispatchToProps(dispatch) {
  return {
    onIncrement: () => dispatch(increment())
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);
```

`react-redux/connect()`の引数にReduxのグローバルな状態とコンポーネントのプロパティをマップする関数と、コンポーネントのプロパティをReduxのActionにマップする関数がある。`Counter.value`に`state.counter`を、`Counter.onIncrement`に`dispatch(increment()`をバインドしてる、ということか。ということは、`App.js`の`select`も大事。

```javascript:App.js
// Which props do we want to inject, given the global state?
// Note: use https://github.com/faassen/reselect for better performance.
function select(state) {
  return {
    visibleTodos: selectTodos(state.todos, state.visibilityFilter),
    visibilityFilter: state.visibilityFilter
  };
}
```

`App.visibleTodos`に`selectTodos()の結果`を、`App.visibilityFilter`に`state.visibilityFilter`を割り当てる。

`redux#bindActionCreators()`はActionを返すメソッド群をそれぞれ`dispatch()`でラップし、さらに1つのオブジェクトにまとめあげるユーティリティ。

### 2日目で学んだこと

- Reactのチュートリアルはわかりやすい
- reduxのstoreとReactのコンポーネントのバインディング方法もわかった
- ボイラープレートベースでいいから簡単なアプリを作成したい

### 関連エントリ

- [3日目はこちら](http://qiita.com/kompiro/items/867c55b32df57cff15bb)