【React】ToDoアプリを発展させよう【非同期処理】

#:star: 関連
* [Reactの環境構築を最短で行う](http://qiita.com/mikan3rd/items/496a87fc8b76263d0a91)
* [【React】ToDoアプリを作ってみよう【前編】](http://qiita.com/mikan3rd/items/20152cdd63a708264a9e)
* [【React】ToDoアプリを作ってみよう【後編】](http://qiita.com/mikan3rd/items/0a1be8fa9f349735cf4a)
* [【React】ToDoアプリを発展させよう【非同期処理】](http://qiita.com/mikan3rd/items/41e4a94a50fa6676542a)←ここ

#:star: 目標
* Reactで作ったToDoアプリに機能を付け足していきます。
* 今回は起動時に非同期処理でTodoをsetするようにします。
* 見本：[GitHub](https://github.com/mikan3rd/todo-list.react.js)

![todo-app4.gif](https://qiita-image-store.s3.amazonaws.com/0/178077/e23e6b23-4ed7-1ee0-df6b-372c382463ec.gif)


#:star: 手順
##:pencil: データ取得先を用意する
###:pencil2: data.json
````json:public/data.json
[
  {
    "title": "Hello, React!",
    "desc": "Reactはじめました"
  },
  {
    "title": "Hello, Redux!",
    "desc": "Reduxもはじめました"
  }
]

````
* publicディレクトリにデータの入ったjsonファイルを作成します。ここからデータを取得することで、模擬的に非同期処理を実装します。
* titleとdesc以外は決まった値なので、App.jsでsetするようにしましょう。

###:pencil2: App.js
````js:src/App.js
...

  constructor() {
    super()
    const todos = []
    this.state = {
      isLoading: false,
      hasError: false,
      todos: todos,
      countTodo: todos.length + 1,
    }
  }

...
````
* constructorのTodoの中身を消去しておきます。
* これから使うisLoadingとhasErrorをstateに追加します。

##:pencil: 非同期処理の実装
###:pencil2: App.js
````js:src/App.js
...

  fetchData(url) {
    this.setState({ isLoading: true })
    fetch(url)
      .then((response) => {
        console.log(response)
        if (!response.ok) {
          throw Error(response.statusText);
        }
        this.setState({ isLoading: false })
        return response
      })
      .then((response) => response.json())
      .then((data) => {
        let countTodo = this.state.countTodo
        const todos = data.map(data => {
          const todo = Object.assign({}, data, { id: countTodo++, done: false })
          return todo
        })
        this.setState({ todos, countTodo })
      })
      .catch(() => this.setState({ hasError: true }))
  }

...
````
* 関数**fetchData()**を定義し、非同期処理の中身を記述します。まず、　`isLoading: true`というstateをsetし、データロード中であることを定めます。
* **fetch()メソッド**の**.then()**を使うことで段階を踏んで処理ができます。
* １つ目の.thenではresponseのokプロパティでアクセスの判定をします。trueの場合は正常にアクセスできているので、`isLoading`をfalseにします。
* ２つ目の.thenでは.json()メソッドでresponseがjsonであることを定義します。一行のアロー関数なので自動的にreturnされています。
* ３つ目の.thenでは渡されたjsonデータをもとにtodoを組み立てています。**Object.assign()**は第1引数を`{}`とすることで、第２引数以降をmerge(結合)した新しいObjectを生成します。
* どこかで処理がうまくいかなかった時、　catch()が呼ばれるため、`hasError: true`というstateをsetします。

###:pencil2: App.js
````js:src/App.js
...

        <TodoList
          todos={this.state.todos}
          setTodoStatus={this.setTodoStatus.bind(this)}
          isLoading={this.state.isLoading}
          hasError={this.state.hasError}
          />

...
````
* TodoListコンポーネントにisLoadingとhasErrorのStateを渡すのを忘れないように。

###:pencil2: TodoList.js
````js:TodoList.js
...

  render() {

    if (this.props.isLoading) {
      return <h2>loading . . . </h2>;
    }

    if (this.props.hasError) {
      return <h2>error</h2>;
    }

...
````
* renderの中にisLoadingかhassErrorがtrueだった時の処理を追記します。

##:pencil: 非同期処理を起動時に呼び出す
###:pencil2: App.js
````js:src/App.js
...

  componentDidMount() {
    this.fetchData('data.json');
  }

...
````
* 最後にfetchData()を起動時に呼び出すようにします。**componentDidMount()**はcomponentがDOMに追加された後で自動的に呼ばれるため、これを利用しましょう。
* この中で先ほど定義したfetchData()を呼び出し、引数に`data.json`を渡します。
* これで、起動時に一瞬「Loading...」が表示されてTodoが表示されます。jsonの書き方がおかしかったりすると「Error」が表示されます。

###:star2: 以上で非同期処理は完成です！
![todo-app9.gif](https://qiita-image-store.s3.amazonaws.com/0/178077/f51ac56c-e9e6-5953-5713-f09bee5d8bca.gif)

* 内容に不備等ありましたら、お手数ですがコメントにてお願いします。

#:star: 参考
* [ReactのRedux非同期処理がサルでも分かる超解説](http://qiita.com/jabba/items/5ebea8d19809a4e0f8c6)