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

  • 1
    いいね
  • 0
    コメント

:star: 関連

:star: 目標

  • Reactで作ったToDoアプリに機能を付け足していきます。
  • 今回は起動時に非同期処理でTodoをsetするようにします。
  • 見本:GitHub

todo-app4.gif

:star: 手順

:pencil: データ取得先を用意する

:pencil2: data.json

public/data.json
[
  {
    "title": "Hello, React!",
    "desc": "Reactはじめました"
  },
  {
    "title": "Hello, Redux!",
    "desc": "Reduxもはじめました"
  }
]

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

:pencil2: App.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

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

:pencil2: App.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

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

src/App.js
...

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

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

:star2: 以上で非同期処理は完成です!

todo-app9.gif

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

:star: 参考