Reactコンポーネントの状態で初心者がしてしまう3つの間違い

>本記事は、[Tyler Hawkins](https://thawkin3.medium.com/)氏による「[3 Mistakes Junior Developers Make With a React Component's State](https://medium.com/better-programming/3-mistakes-junior-developers-make-with-a-react-components-state-997043291b54)」（2020年6月26日公開）の和訳を、著者の許可を得て掲載しているものです。

#Reactコンポーネントの状態で初心者がしてしまう3つの間違い

>そして、それをやめる方法

![Image for post](https://miro.medium.com/max/630/0*lIYGOdkZoD9RVFH4)

>Photo by [Jamie Street](https://unsplash.com/@jamie452?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral).

私がウェブ開発で好きなことの1つは、常に新しいことを学ぶことができるということです。さまざまなプログラミング言語、ライブラリ、フレームワークを習得するのに一生を費やしても、すべてを理解することはできません。

私達は皆学び続けているので、それは間違いをおかしやすいということも意味します。でもそれでいいのです。目標は、より良くなること、より良くあることです。間違ってもそこから学べば、あなたは素晴らしいことをしているということです！しかし、新しいことを学ばず、同じ間違いを繰り返しているとしたら...あなたのキャリアが停滞しているような気がします。

そのような考えで、コードレビューでよく目にする間違いを3つご紹介します。Reactコンポーネントの状態の処理で、初心者がよくする間違いです。それぞれをよく観察し、修正する方法について説明します。

##1. 状態を直接変更する

コンポーネントの状態を変更する時は、現在の状態を直接変更するのではなく、変更を加えた状態の新しいコピーを返すことが重要です。コンポーネントの状態を誤って変更すると、Reactの差分アルゴリズムが変更を取得できず、コンポーネントが適切に更新されません。

例を見てみましょう。次のような状態があるとします。

```react
this.state = {
  colors: ['red', 'green', 'blue']
}
```

次に、この配列に`yellow`を追加します。こうしたくなるかもしれません。

```react
this.state.colors.push('yellow')
```

またはこのように。

```react
this.state.colors = [...this.state.colors, 'yellow']
```

しかし、どちらの方法も正しくありません！クラスコンポーネントの状態を更新する時は、常に`setState`メソッドを使用し、オブジェクトを変化させないように注意する必要があります。

配列に要素を追加する正しい方法は、次の通りです。

```react
this.setState(prevState => ({ colors: [...prevState.colors, 'yellow'] }))
```

これが2番目の間違いにも関係してきます。次を見てみましょう。

##2. 関数を使用せずに、前の状態に依存する状態を設定する

`setState`メソッドを使用する方法は2つあります。1つ目は、引数としてオブジェクトを指定する方法です。2つ目は、引数として関数を指定する方法です。では、どのような場合にどちらを使用すべきなのでしょうか？

例えば、有効または無効にできるボタンがある場合、ブール値を保持する`isDisabled`という状態の一部があるかもしれません。ボタンを有効から無効に切り替えたい場合は、引数としてオブジェクトを指定して、こう書きたくなるかもしれません。

```react
this.setState({ isDisabled: !this.state.isDisabled })
```

これの何が問題なのでしょうか？問題は、Reactの状態の更新はバッチ処理が可能であるという事実にあります。つまり、単一の更新サイクルで複数の状態更新が発生する可能性があります。更新がバッチ処理され、有効・無効の状態に複数の更新があった場合、最終的な結果は期待したものではない可能性があります。

状態を更新するより良い方法は、引数として前の状態の関数を指定することです。

```react
this.setState(prevState => ({ isDisabled: !prevState.isDisabled }))
```

これで、状態の更新がバッチ処理され、有効・無効の状態に複数の更新が同時に行われた場合でも、それぞれの更新は正しい前の状態に依存するため、常に期待通りの結果が得られます。

カウンタのインクリメントのような場合にも、も同じことが言えます。

こうしないで

```react
this.setState({ counterValue: this.state.counterValue + 1 })
```

こうしてください。

```react
this.setState(prevState => ({ counterValue: prevState.counterValue + 1}))
```

**ここで重要なのは、新しい状態が古い状態の値に依存している場合は、常に関数を引数として指定する必要があるということです。古い状態の値に依存しない値を設定する場合は、引数としてオブジェクトを指定できます。**

##3. setStateが非同期であることを忘れる

最後に、`setState`が非同期メソッドであるということを覚えておくことが重要です。例として、次のような状態のコンポーネントがあるとしましょう。

```react
this.state = { name: 'John' }
```

そして、状態を更新してコンソールに状態を記録するメソッドがあるとします。

```react
this.setState({ name: 'Matt' })
console.log(this.state.name)
```

これで「`Matt`」が記録されると思うかもしれませんが、違います。「`John`」が記録されます！

これは、`setState`が非同期だからです。つまり、`setState`を呼び出す行に到達すると状態の更新が開始されますが、非同期コードは非ブロッキングであるため、その下のコードは続けて実行されます。

状態が更新された**後**に実行する必要のあるコードがある場合、Reactには、更新が完了すると実行されるコールバック関数があります。

更新後に現在の状態を記録する正しい方法は、次の通りです。

```react
this.setState({ name: 'Matt' }, () => console.log(this.state.name))
```

ずっと良くなりました！これで、期待通りに「`Matt`」が正しく記録されます。

##まとめ

これがよくある3つの間違いとその修正方法です！覚えておいてください。間違っても大丈夫。あなたは学び続けています。私もです。私達は皆学び続けています。一緒に学び続け、より良くなりましょう。

更新：同じ概念を、関数コンポーネントとフックを使用して学びたいですか？私のフォローアップ記事を読んでください！

>[**React関数コンポーネントの状態で初心者がしてしまう3つの間違い**](https://medium.com/better-programming/3-mistakes-junior-developers-make-with-react-function-component-state-8a744ab99a0d)

#
>Zack Shapiroに謝意を表します。

##翻訳協力
Original Author: [Tyler Hawkins](https://thawkin3.medium.com/)
Original Article: [3 Mistakes Junior Developers Make With a React Component's State](https://medium.com/better-programming/3-mistakes-junior-developers-make-with-a-react-components-state-997043291b54)
Thank you for letting us share your knowledge!

この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。
選定担当: @gracen
翻訳担当: @gracen
監査担当: -
公開担当: @gracen

##ご意見・ご感想をお待ちしております
今回の記事はいかがでしたか？
・こういう記事が読みたい
・こういうところが良かった
・こうした方が良いのではないか
などなど、率直なご意見を募集しております。
頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に
コメント欄にてご投稿ください。[Twitter](https://twitter.com/BabyDegu?lang=ja)でもご意見を受け付けております。
皆様のメッセージをお待ちしております。