React.jsのプロジェクトはじめの一歩 〜簡単なタイムラインの実装からRedux導入、そしてHTTP APIまで〜


ここでの手順はMacOSXでHomebrewがインストール済みであると仮定しています。
Homebrewが入ってない方は、[Homebrewインストール手順](http://qiita.com/rabbit1013/items/1494cf345ff172c3b9cd)を参考に入れておいてください。
WindowsやLinuxは適当に... Linuxならnode入れたらあとは同じだろうけど、Windowsはわかりません。。


## nodeとcreate-react-appのインストール

```
$ brew install node
$ sudo npm install -g create-react-app
```

## react.jsプロジェクトを作る

プロジェクト名を `timeline` として作る。

```
$ create-react-app timeline
$ cd timeline
```

## 依存ライブラリのインストール

後々必要になるだろうから、サーバーと通信するために `react-fetch` と `redux-thunk` もインストールしておく。

```
$ npm install --save react-fetch redux-thunk
```

あと、後で使うのでルーティングライブラリも入れておく

```
$ npm install --save react-router react-router-dom
```

それと最後にReduxで状態管理するようにするので、 `redux` と `react-redux` も入れておく

```
$ npm install --save redux react-redux
```

あ、それと型チェックライブラリの `prop-types` も。

```
$ npm install --save prop-types
```


## nodeサーバーの起動

```
$ npm start
```

ブラウザにフォーカスが移って画面が表示されるのを確認。

## App.jsの編集

生成直後のApp.js

```js:src/App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

```

ここに、入力用のテキストエリアを追加。さらに入力した内容がリアルタイムにテキストエリアの下に表示されるようにする。

1. `constructor(props)` を定義して、 `this.state` としてstate変数を定義する。
2. `render()` を変更して、テキストボックスを配置。 `this.state.text` の内容を常に反映するようにする。
3. また、テキストボックスに `onChange` を定義して、内容を変更された時に `this.setState` を呼んで、state変数の内容を変更する。

```javascript:src/App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: ""
    };
  }

  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          今何してる？
          <input type="text" value={ this.state.text }
            onChange={e => {this.setState({text: e.target.value})}}/>
        </p>
        <p>{this.state.text}</p>
      </div>
    );
  }
}

export default App;

```

さらに、ツイートするとタイムラインに追加されるようにします。

1. `constructor` 内で、 `this.state` に `timeline` という配列を入れる変数を追加します。
2. `<input type="button">` を追加して、クリックされた場合に `this.state.timeline` に`this.state.text` のテキストを（先頭に）追加。即座にタイムラインに反映される。

```javascript:src/App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "",
      timeline: []
    };
  }

  render() {
    var tweets = [];

    for (var i in this.state.timeline) {
      tweets.push(<li>{this.state.timeline[i]}</li>)
    }

    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          今何してる？
          <input type="text" value={ this.state.text }
            onChange={e => {this.setState({text: e.target.value})}}/>
        </p>
        <p>
          {this.state.text}
          <input type="button" value="ツイート" onClick={e => {
            var array = this.state.timeline;
            array.unshift(this.state.text);
            this.setState({timeline: array});
            this.setState({text: ""});
          }} />
        </p>

        <ul>{tweets}</ul>
      </div>
    );
  }
}

export default App;

```

## Timelineの部分を別ファイルに分割する

`App.js` と同じ階層に `Timeline.js` を新たに作る。
内容な以下のような感じ。

ポイントとしては、親(App.js)が持っているtimeline配列をpropsで受け取るようにしているところ。

```javascript:src/Timeline.js
import React, { Component } from 'react';

class Timeline extends Component {
  constructor(props) {
    super(props);
    this.state = {
      timeline: props.timeline
    }
  }

  render() {
    let tweets = [];

    for (let i in this.state.timeline) {
      tweets.push(<li>{this.state.timeline[i]}</li>)
    }

    return <ul>{tweets}</ul>
  }
}

export default Timeline;
```

`App.js` も少し変更.

1. ```import Timeline from './Timeline.js'``` を追加して、上の `Timeline.js` を読み込む.
2. render()でタイムラインを描画していた部分を ```<Timeline timeline={this.state.timeline} />```にして、先ほど定義した `Timeline` を呼び出すようにする。
3. `Timeline` を呼び出すタグで `timeline={this.state.timeline}` としてtimeline配列を渡し、受け取った `Timeline.js` 内では `props.timeline` として使用する。

```javascript:src/App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Timeline from './Timeline.js'

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "",
      timeline: []
    };
  }

  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          今何してる？
          <input type="text" value={ this.state.text }
            onChange={e => {this.setState({text: e.target.value})}}/>
        </p>
        <p>
          {this.state.text}
          <input type="button" value="ツイート" onClick={e => {
            var array = this.state.timeline;
            array.unshift(this.state.text);
            this.setState({timeline: array});
            this.setState({text: ""});
          }} />
        </p>

        <Timeline timeline={this.state.timeline} />
      </div>
    );
  }
}

export default App;
```

最終的な状態
![スクリーンショット 2017-08-29 12.48.19.png](https://qiita-image-store.s3.amazonaws.com/0/13835/0ed83646-8526-d203-030e-a791381de218.png "スクリーンショット 2017-08-29 12.48.19.png")

## react-routerによるルート制御

さて、今度はこれを、URLによって表示されるページが変わるようにします。
目標としては、 `http://localhost:3000/` にアクセスされた場合は先ほどの `App` で実装されたページを表示し、 `http://localhost:3000/about/(なんらかの数字)` が表示された場合には、このサイトの説明が表示されるページへジャンプします。

### index.jsの編集

こちらが変更前。

```javascript:src/index.js(変更前)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
```

そして変更後がこちら。

1. `import { BrowserRouter } from 'react-router-dom';` としてBrowserRouterを読み込む。
2. ReactDOM.renderで、 `<BrowserRouter><App /></BrowserRouter>` としてブラウザ実行時のルーター内に `App`を配置する.

```javascript:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'));

registerServiceWorker();
```

### App.jsの変更

続いて、 `App.js` を編集して、App.js内では `<Header />` と `<Main />` の呼び出しのみとします。
（HeaderとMainは後で作ります）

```javascript:src/App.js
import React, { Component } from 'react';
import './App.css';
import Header from './Header.js';
import Main from './Main.js';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "",
      timeline: []
    };
  }

  render() {
    return (
      <div className="App">
        <Header />
        <Main />
      </div>
    );
  }
}

export default App;
```

### Header.jsの新規作成

この `App.js` 内から呼び出される `Header.js` と `Main.js` を作ります。
まずは `Header.js` 。 内容は、ページの遷移に関係なく表示する、ページ上部のロゴと、各ページへのリンクです。

```javascript:src/Header.js(新規に作成)
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class Header extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <div className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h2 style={{marginTop: "4px"}}>Welcome to React</h2>

        <a href="/hello">ご挨拶</a>:
        <a href="/about/1">1について/1</a>:
        <a href="/about/2">2について/2</a>:
        <a href="/">タイムライン</a>:
      </div>
    );
  }
}

export default Header;
```

### Main.jsの新規作成

続いて `Main.js` 。こちらは、ここからさらに、呼び出しURLに応じて表示する内容を変えます。
なお、この実装で重要なところは、 `render()` 関数内で、 `<Switch>` を使ってルーティングを制御しているところです。

それと、 `About` へのルーティングのところで、pathを `/about/:number` としています。このように `:(コロン)` を先頭につけると、URL呼び出し時にその位置に指定された値をコンポーネント内で受け取ることができます。
たとえば下記のソースの場合、 `Abount.js` 内で `this.props.match.params.number` として `:number` の位置で指定された値を受け取ることができます。

```javascript:src/Main.js
import React, { Component } from 'react';
import './App.css';
import { Switch, Route } from 'react-router-dom';
import TimelineApp from './TimelineApp.js';
import About from './About.js';
import Hello from './Hello.js';

class Main extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "",
      timeline: []
    };
  }

  render() {
    return (
      <Switch>
        <Route exact path='/' component={TimelineApp} />
        <Route exact path='/hello' component={Hello} />
        <Route exact path='/about/:number' component={About} />
      </Switch>
    );
  }
}

export default Main;
```

### TimelineApp.jsの新規作成

さて、ここで先ほど `Main.js` で指定された `TimelineApp.js` を実装します。

```javascript:src/TimelineApp.js
import React, { Component } from 'react';
import './App.css';
import Timeline from './Timeline.js'

class TimelineApp extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "",
      timeline: []
    };
  }

  render() {
    return (
      <div className="Main">
        <p className="App-intro">
          今何してる？
          <input type="text" value={ this.state.text }
            onChange={e => {this.setState({text: e.target.value})}}/>
        </p>
        <p>
          {this.state.text}
          <input type="button" value="ツイート" onClick={e => {
            var array = this.state.timeline;
            array.unshift(this.state.text);
            this.setState({timeline: array});
            this.setState({text: ""});
          }} />
        </p>

        <Timeline timeline={this.state.timeline} />
      </div>
    );
  }
}

export default TimelineApp;
```

### Hello.jsの新規作成

さらに、内容はまったく無いのですが、 `Hello.js` というコンポーネントを実装して `/hello` をブラウザで開いたら上記タイムラインとは違うページが開くようにします。

```javascript:src/Hello.js
import React, { Component } from 'react';
import './App.css';

class Hello extends Component {
  constructor(props) {
    super(props);
    this.state = {}
  }

  render() {
    return (
      <div className="Hello">
        <h1>ここはHello</h1>
        <p>うまくルーティングできたかな？</p>
      </div>
    );
  }
}

export default Hello;
```

### About.jsの新規作成

そして `About.js` を新規で作成します。これが `Hello.js` と異なるところは、 `/abount/3` のようにURLにパラメータがあるとそれを受け取ってページ内に表示するところです。

```javascript:src/About.js
import React, { Component } from 'react';
import './App.css';

class About extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    return (
      <div className="App">
        <p className="App-intro">
          このアプリケーションはReactの勉強用に作りました。<br /><br />
          ちなみに渡された値は{ this.props.match.params.number }
        </p>
      </div>
    );
  }
}

export default About;
```

## babelによる互換性

なお、 `react-create-app` でアプリケーションを作ると、ECMAScript+JSXで書いたアプリが `npm run build` 時に自動的にIEにも互換性のあるJSで書き出されます（書き出し先はbuildディレクトリ内）。

ただ、 `npm start` で起動した場合はこのビルドが行われませんので、 `serve` をインストールして、そちらでサーバーを起動し、JSにコンパイルされたソースを使ってアプリを実行します。

```
$ npm run build
$ npm install -g serve (serveをインストール)
$ serve -s build (buildディレクトリをrootディレクトリとしてサーバーを起動)

   ┌───────────────────────────────────────────────────┐
   │                                                   │
   │   Serving!                                        │
   │                                                   │
   │   - Local:            http://localhost:5000       │
   │   - On Your Network:  http://xxx.xx.xx.xxx:5000   │
   │                                                   │
   │   Copied local address to clipboard!              │
   │                                                   │
   └───────────────────────────────────────────────────┘
```

ブラウザで[http://localhost:5000](http://localhost:5000)にアクセス。

この時点でのソースコードは[hiroeorz/timelineのrouterブランチ](https://github.com/hiroeorz/timeline/tree/router)にあります。

## Reduxの導入

これまでは、それぞれのコンポーネントに `state` をもたせて、 `this.setState()` として保持している値を更新したりしてきましたが、ここでFluxの考え方を導入します。

Fluxの考え方については私がごちゃごちゃいうより解りやすい記事があるのでそちらを参照してください。
[Fluxとはなんなのか](http://qiita.com/knhr__/items/5fec7571dab80e2dcd92)

Flux自体は考え方なのですが、このFluxの考え方をもとに作られた[Redux]()というフレームワーク(?)があるので、これを使います。
[結局FluxやらReduxやらって何なのか個人的なまとめ](http://qiita.com/syossan27/items/7e1b2e07ac68b96bdaa7)

### reducerの実装
まずは以下のようにreducerを実装します。

```javascript:src/reducer.js
const initialState = {
  timeline: [],
  text: ""
}

export default function reducer(state = initialState, action) {
  switch(action.type) {

    /* ツイートを投稿する */
    case 'POST_TWEET':
      return {
        ...state,
        text: "",
        timeline: [action.text, ...state.timeline]
      };

    /* ツイートを書き込むテキストボックスの内容を保持する */
    case 'CHANGE_TEXT':
      return {
        ...state,
        text: action.text
      };

    default:
      return state
  }
}
```

内容は、状態保持する `state` と引数として渡された `action` に応じて分岐して処理するものです。
actionには、この後実装するactionで必ず `type` をキーとして渡すことになっており、これを元にどのような処理を行うのかを記述します。で、ここで大切なのは、 `state` の内容を更新したい場合、 `state` を更新するのではなく、必ず新たに生成した `state` を返すということです。

ここでちょっと変わった事をしています。というか、多分フロントをやっている人には常識なんでしょうけど、配列に要素を追加するのに `[action.text, ...state.timeline]` としており、これはspread syntaxという書き方なのだそうです。この書き方をすると、ある配列、またはオブジェクトに新たな要素を追加することができます。

例として

```javascript:配列への要素追加
let array1 = [1, 2, 3];
let array2 = [...array1, 4, 5, 6];

//=> [1, 2, 3, 4, 5, 6]
```

```javascript:オブジェクトへの要素追加
let obj1 = {name: "hiroe", age: 42};
let obj2 = {...obj1, nickname: "hiropon"};

//=> {name: "hiroe", age: 42, nickname: "hiropon"}
```

て感じです。で、こいつのいいところは、もとのオブジェクトを変更せずに、あらたなオブジェクトを生成してくれるようで、reduxのreducerにはうってつけのようです。

### actionの実装

actionはこんな感じで実装。

```javascript:src/action.js
export function mapStateToProps(state) {
  return state;
}

export function mapDispatchToProps(dispatch) {
  return {
    /* ツイートを投稿する */
    postTweet: (text) => {
      dispatch( {type: 'POST_TWEET', text: text} );
    },

    /* ツイートを書き込むテキストボックスの内容をstate.textに保持する */
    changeText: (text) => {
      dispatch( {type: 'CHANGE_TEXT', text: text} );
    }
  }
}
```

`mapStateToProps` と `mapDispatchToProps` の２つを実装しています。
mapDispatchToProps内に呼ばれた関数をactionオブジェクトに変換する関数を実装しています。ここでの処理はあくまで、呼ばれた関数をactionオブジェクトに変換するだけという点に注意が必要です。
こうしておくと、先ほどのreducerにオブジェクトが渡るので、実際にstateを更新する処理はreducer側で書くことになります。

### storeの生成

1. index.jsを編集し、このアプリケーション全体で使用する `store` を生成します( `const store = createStore(reducer);` )。
2. さらに、`Provider` タグでレンダリングする全体を囲んで、アプリケーション全体で `store` を使用できるようにします。

```javascript:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { BrowserRouter } from 'react-router-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './reducer.js';

const store = createStore(reducer);

ReactDOM.render((
    <Provider store={store} >
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
), document.getElementById('root'));

registerServiceWorker();
```


### connectによるReactとReduxの接続

最後に、connectによってReactとReduxを接続します。
やることは、Reduxによって管理されている `state` から値を取得したり値を更新するコンポーネントで、connect関数によって先ほどのactionとコンポーネントのつなぎこみをします。
ソースは以下のようになりました。

#### Timeline.jsの編集

1. 今まで `export default Timeline` としていたところを `export default connect(mapStateToProps)(Timeline)` に変更しています（ `mapStateToProps` は `src/action.js` 内で実装しています。）
2. また、stateの初期化が必要なくなったため、constructorを削除しています。そのほかの `App.js` や `About.js` 等、ほかのコンポーネントにあったconstructorもすべて削除しました。reduxを使う場合、状態の保持はstoreで行われるので各コンポーネントでのstateの定義は必要なくなります。

```javascript:src/Timeline.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { mapStateToProps } from './action.js';

class Timeline extends Component {
  render() {
    let tweets = [];

    for (let i in this.props.timeline) {
      tweets.push(<li key={i}>{this.props.timeline[i]}</li>)
    }

    return <ul>{tweets}</ul>
  }
}

export default connect(mapStateToProps)(Timeline);
```

#### TimelineApp.jsの編集

こちらも同様に `connect` を使ってactionとコンポーネントを繋いでいます。
先ほどの `Timeline.js` と異なる点は、繋ぎ込みの際に、 `mapDispatchToProps` も繋いでいる点です。

これは、 `Timeline.js` ではstateの値の参照だけだったのに対して、 `TimelineApp.js` では `state` に保存された値を更新するための関数を呼び出すからです。

```javascript:src/TimelineApp.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import './App.css';
import Timeline from './Timeline.js'
import { mapStateToProps, mapDispatchToProps } from './action.js';

class TimelineApp extends Component {
  render() {
    return (
      <div className="Main">
        <p className="App-intro">
          今何してる？
          <input type="text" value={ this.props.text }
            onChange={ (e) => this.props.changeText(e.target.value)} />
        </p>
        <p>
          {this.props.text}
          <button onClick={ () => this.props.postTweet(this.props.text) }>
            つぶやく
          </button>
        </p>

        <Timeline timeline={this.props.timeline} />
      </div>
    );
  }
}

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

以上で完了です。ブラウザで [http://localhost:3000](http://localhost:3000)にアクセスして動作確認します。
reduxを使うことで、状態管理を一箇所にあつめることができ、各コンポーネントで管理する必要がないのでソースがすっきりしました。

この時点でのソースコードは[hiroeorz/timelineのreduxブランチ](https://github.com/hiroeorz/timeline/tree/redux)にあります。

## PropTypesで型チェック

reactでは、 `PropTypes` というライブラリを使って、propsとして外部から渡されるオブジェクトの型チェックを行うことができます。

構文は、例えば `App` コンポーネントにに渡されるpropsの型チェックの場合は

```javascript
App.propTypes = {
  text: PropTypes.string,
  timeline: PropTypes.arrayOf(PropTypes.string)
}
```

てな具合です。２つめの `timeline` は、文字列の配列なので、 `PropTypes.string` を `PropTypes.arrayOf()` で囲っています。

さて、ここまでのコードにproptypesを適用すると、propsから値を受け取って何かするのは `Timeline` と `TimelineApp` の２つのコンポーネントですのでここで定義します。

まずは `Timeline.js` から。

最初に `import PropTypes from 'prop-types';` としてライブラリをインポートし、後半で `TimelineApp.propTypes = {...}` として `TimelineApp` コンポーネントで使用するプロパティの型チェックをおこなっています。

また、必須のプロパティについては `isRequired` をつけておきます。あってもなくてもいいなら必要ないです。
PropTypesの詳しい使い方については[Typechecking With PropTypes](https://facebook.github.io/react/docs/typechecking-with-proptypes.html)に詳しく書いてあります。

```javascript:src/TimelineApp.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import './App.css';
import Timeline from './Timeline.js'
import { mapStateToProps, mapDispatchToProps } from './action.js';

class TimelineApp extends Component {
  render() {
    return (
      <div className="Main">
        <p className="App-intro">
          今何してる？
          <input type="text" value={ this.props.text }
            onChange={ (e) => this.props.changeText(e.target.value)} />
        </p>
        <p>
          {this.props.text}
          <button onClick={ () => this.props.postTweet(this.props.text) }>
            つぶやく
          </button>
        </p>

        <Timeline timeline={this.props.timeline} />
      </div>
    );
  }
}

TimelineApp.propTypes = {
  text: PropTypes.string.isRequired,
  timeline: PropTypes.arrayOf(PropTypes.string).isRequired
}

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

同様に `Timeline` コンポーネント。

```javascript:Timeline
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { mapStateToProps } from './action.js';

class Timeline extends Component {
  render() {
    let tweets = [];

    for (let i in this.props.timeline) {
      tweets.push(<li key={i}>{this.props.timeline[i]}</li>)
    }

    return <ul>{tweets}</ul>
  }
}

Timeline.propTypes = {
  timeline: PropTypes.arrayOf(PropTypes.string).isRequired
}

export default connect(mapStateToProps)(Timeline);
```

こちらは `props.timeline` しか使わないので、これだけ型チェック。

なお、型チェックに失敗してもユーザーにはエラーが表示されず、デベロッパーツールで以下のように警告されます。
![スクリーンショット 2017-09-05 22.25.15.png](https://qiita-image-store.s3.amazonaws.com/0/13835/023f430b-f886-4816-3ecb-8996fcc65c03.png "スクリーンショット 2017-09-05 22.25.15.png")

## fetch + redux-thunk によるHTTP API連携
さて、reduxの導入、PropTypesの型検査まできましたが、最後にWebアプリでは必須と言えるHTTP API連携の実装をします。
調査したところでは、いくつかの方法があるようですが、私が見た中で最も分かりやすかった `fetch` と `redux-thunk` を使った方法で実装して見ます。

fetchはreact標準のHTTPクライアントのようで、基本的な使い方は

```javascript:fetchの基本的使い方
fetch('http://localhost:3000/hiroe.json').then(
  response => {
    response.json().then(
      (json) => dispatch({type: 'HUMAN', human: json})
    )
  },
  error =>
    dispatch({type: 'HUMAN_ERROR', reason: error})
)
```

て感じになるようです。fetchに引数としてURLを渡し、その戻りに対する処理内容を成功の場合と失敗の場合で分けて関数で渡します。

で、redux-thunkがなにかというと、githubのREADMEからMotivationをGoogleさんに和訳してもらうと

```
Redux Thunkミドルウェアを使用すると、アクションではなく関数を返すアクションクリエータを作成できます。
thunkは、アクションのディスパッチを遅らせるために、または特定の条件が満たされた場合にのみディスパッチ
するために使用できます。
内部関数は、ストア・メソッドdispatchおよびgetStateをパラメータとして受け取ります。
```

というわけで、thunkを使うとactionクリエーターの戻り値に、処理した結果ではなく、処理内容を包んだ関数を返すようにし、これを非同期で実行できるようになるようです。
なので、fetchとthunkを組み合わせることで、fetchでHTTP APIを叩き、非同期にデータを受け取ったらjsonパースしてディスパッチする、というようなことが実現するようです。

なにはともあれ作ってみます。
HTTP APIは通常はnodeやRailsなどで動的な値を返すサーバーを組むでしょうが、だるいので今回は以下のようなファイルをプロジェクトの `public/hiroe.json` として新規作成して内容が固定のHTTP APIとして使用します。

```json:public/hiroe.json
{
  "name": "ひろえ",
  "age": 41,
  "nickname": "ひろぽん"
}
```

これで、[http://localhost:3000/hiroe.json](http://localhost:3000/hiroe.json)にブラウザでアクセスすると、このファイルの内容が表示されます。


また、先ほどまでは `store` を `src/index.js` 内で定義していましたが、これを `store.js` という別ファイルに移し、 `index.js` と `action.js` の両方から使えるようにします。
また、storeを作る際に、引数に `applyMiddleware(thunk)` として `thunk` をミドルウエアとして指定しています。

```js:src/store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer.js';

export const store = createStore(reducer, applyMiddleware(thunk));
```

これを `src/index.js` から読み込むように `src/index.js` を編集します。

```js:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from './store.js';

ReactDOM.render((
    <Provider store={store} >
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </Provider>
), document.getElementById('root'));

registerServiceWorker();
```

さて、今回は新たに `src/Human.js` というコンポーネントを作って、「ボタンを押したらサーバーから取得した内容を表示する」ということをします。

まずは以下のように `src/Human.js` を作成します。

```javascript:src/Human.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import './App.css';
import { mapStateToProps, mapDispatchToProps } from './action.js';

class Human extends Component {
  render() {
    return (
      <div className="Main">
        <button onClick={ () => this.props.searchHuman() }>表示</button>

        <table>
          <tbody>
            <tr>
              <th>名前</th> <td>{ this.props.human.name }</td>
            </tr>
            <tr>
              <th>年齢</th> <td>{ this.props.human.age }歳</td>
            </tr>
            <tr>
              <th>ニックネーム</th> <td>{ this.props.human.nickname }</td>
            </tr>
          </tbody>
        </table>

      </div>
    );
  }
}

Human.propTypes = {
  human: PropTypes.shape({
    name: PropTypes.string.isRequired,
    age: PropTypes.number.isRequired,
    nickname: PropTypes.string.isRequired
  }).isRequired
}

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

「表示」ボタンをクリックすると、サーバーから取得した内容が `props` に渡され、これをテーブルに表示します。

続いて、各ページへのルーティングを記述していた `src/Main.js` にて、 `/human` にアクセスがあったら `Human` をコンポーネントとしてページを表示するようにルートを追加します。

```javascript:src/Main.js
import React, { Component } from 'react';
import './App.css';
import { Switch, Route } from 'react-router-dom';
import TimelineApp from './TimelineApp.js';
import About from './About.js';
import Hello from './Hello.js';
import Human from './Human.js';

class Main extends Component {
  render() {
    return (
      <Switch>
        <Route exact path='/' component={TimelineApp} />
        <Route exact path='/about/:number' component={About} />
        <Route exact path='/hello' component={Hello} />
        <Route exact path='/human' component={Human} />
      </Switch>
    );
  }
}

export default Main;
```

ルートの最後に `Human` を追加しています。

次に、ヘッダ部にリンクを追加します。修正するのは `src/Header.js` です。

```javascript:src/Header.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class Header extends Component {
  render() {
    return (
      <div className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h2 style={{marginTop: "4px"}}>Welcome to React</h2>

        <a href="/hello">ご挨拶</a>:
        <a href="/about/1">1について/1</a>:
        <a href="/about/2">2について/2</a>:
        <a href="/">タイムライン</a>:
        <a href="/human">人間検索</a>:
      </div>
    );
  }
}

export default Header;
```

さて、続いて `src/action.js` を編集し、先ほどの `src/Human.js` においてボタンに設定したアクション（ `onClick={ () => this.props.searchHuman() }` ）を実装します。

```javascript:src/action.js
import {store} from './store.js';

export function mapStateToProps(state) {
  return state;
}

export function mapDispatchToProps(dispatch) {
  return {
    /* ツイートを投稿する */
    postTweet: (text) => {
      dispatch( {type: 'POST_TWEET', text: text} );
    },

    /* ツイートを書き込むテキストボックスの内容をstate.textに保持する */
    changeText: (text) => {
      dispatch( {type: 'CHANGE_TEXT', text: text} );
    },

    /* サーバーからJSONを取得し、その内容を表示する */
    searchHuman: () => {
      let f = function (dispatch) {
        return fetch('http://localhost:3000/hiroe.json').then(
          response => {
            response.json().then(
              (json) => dispatch({type: 'HUMAN', human: json})
            )
          },
          error =>
            dispatch({type: 'HUMAN_ERROR', reason: error})
        )
      }

      store.dispatch(f).then( () => { console.log("Done!"); })
    }

  }
}
```

追加したのは `searchHuman` という関数です。
ここで「HTTP APIをfetchし、取得したJSONをパースし、最後にその内容をdispatchに渡す処理」を記述した関数を `store.dispatch(f)` として渡しておき、完了したらコンソールに `Done!` と表示するようにしています。

続いて `src/reducer.js` を編集します。

```javascript:src/reducer.js
const initialState = {
  timeline: [],
  text: "",
  human: {name: "", age: 0, nickname: ""}
}

export default function reducer(state = initialState, action) {
  switch(action.type) {

    /* ツイートを投稿する */
    case 'POST_TWEET':
      console.log(state);
      return {
        ...state,
        text: "",
        timeline: [action.text, ...state.timeline]
      };

    /* ツイートを書き込むテキストボックスの内容を保持する */
    case 'CHANGE_TEXT':
      return {
        ...state,
        text: action.text
      };

    /* サーバーからJSONを取得し、その内容を表示する */
    case 'HUMAN':
      console.log(action);
      return {
        ...state,
        human: action.human
      };

    /* サーバーからJSONを取得する時に発生したエラー情報をコンソールに表示する */
    case 'HUMAN_ERROR':
      console.log(action);
      return state;

    default:
      return state
  }
}
```

アクションタイプの分岐に `HUMAN` と `HUMAN_ERROR` を追加しています。
さて、これで [http://localhost:3000/human](http://localhost:3000/human)にブラウザでアクセスすると「表示」ボタンが表示されており、ボタンをクリックするとサーバーから取得した内容が表示されます。

![スクリーンショット 2017-09-07 9.57.41.png](https://qiita-image-store.s3.amazonaws.com/0/13835/81ec0ee5-2afa-87ab-f756-1a7394b2995f.png "スクリーンショット 2017-09-07 9.57.41.png")



とりあえずここまで。
初心者なので色々勘違いがあるかもしれません。指摘歓迎しますのでよろしくお願いします。