前回からの続きですが、このページだけ読んでも分かる内容になっています。
コンポーネント内でstateを使用する際の注意点3つ
- state は直接更新せずに、setState() を使用して更新する
- this.state を更新するタイミングはReactが決める
- state のなかの一部の変数が更新されることがある
1. state は直接更新せずに、setState() を使用して更新する
コンポーネント内でstateを使用する場合、最初に実行されるコンストラクタのなかでは state に値を直接代入して初期化できますが、そのあとにstateを更新する際には直接更新はできません。stateを更新する際には直接更新するのではなく、代わりに setState() を使用します。
例えば、以下のコードは誤りです。このコードでは、Reactが state が更新されたことを検知できないために再レンダーされません。Reactは setState() が実行されたことをきっかけに state が更新されたことを検知します。stateを直接更新してしまうと、Reactが再レンダーするきっかけが与えられません。
// 誤った書き方
this.state.comment = 'Hello';
state を変更する際の正しいコードは以下になります。stateを直接変更するのではなく、代わりに setState() を使用します。setState() を使用することで、 Reactが state が更新されたことを検知して、更新された部分を再レンダーできるようになります。
// 正しい書き方
this.setState({comment: 'Hello'});
this.state に直接代入してよい唯一の場所はコンストラクタです。それ以外の場所では setState() を使用して state を更新してください。
2. this.state を更新するタイミングはReactが決める
Reactでは、this.state の更新が非同期に行われることがあります。これは、Reactがパフォーマンスを高めるために、複数の setState() の呼び出しを一度にまとめて処理することがあるからです。そのため、stateの値を求める際には、 this.props、および、this.state の値に依存するべきではありません。
例えば、以下のコードは誤りです。このコードでは、counterをインクリメントするのに this.props や this.state を利用しています。this.props、および、this.state の値を更新するタイミングはReactが決めるため、これらの値に依存したプログラムではカウンターの値が期待しない挙動になる場合があります。
// 誤った書き方
this.setState({
counter: this.state.counter + this.props.increment,
});
正しいコードは以下になります。以下のコードでは、 setState() の第1引数にこの時点でのstateを、第2引数にこの時点でのpropsを指定しています。これらの現時点での引数を元にしてcounterをインクリメントしています。
Reactがどのタイミングで更新するのか分からない this.state や this.props を使うのではなく、何が入っているのか明確な現時点でのstate や 現時点でのprops を引数として与えることで、信頼できる値を使ってカウンターがインクリメントされます。
// 正しい書き方
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
上記のコードをアロー関数を、通常の関数に書き換えると以下になります。上記のコードはES6によるモダンな記法、以下のコードはES5によるレガシーな記法という違いはありますが、コードが実行する内容は同じです。
重要なのは state や props を引数として与えていて、 this.state や this.props を使っていないという点です。以下のコードも正しく動作します。
// 正しい書き方 ※通常の関数を使ったコード
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
3. state のなかの一部の変数が更新されることがある
state には、いくつかの独立した変数を含んでいる場合があります。そして、そのなかの一部の変数が更新されることがあります。
例えば、以下のコードでは、stateは、postsとcommentsという2つの独立した変数を含んでいます。
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
上記の場合、例えば以下のように setState() を別に呼び出して、それぞれの変数を独立して更新できます。
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
上記のコードでは、 this.setState({posts}) は、this.state.posts を完全に置き換えて、this.state.comments をそのまま残します。また、 this.setState({comments}) は、this.state.posts をそのまま残して、this.state.comments を完全に置き換えます。
つまり、ひとつの state に含まれる2つの変数 postsとcomments は、それぞれ独立して更新されます。
コンポーネント内でstateを使用する際の注意点まとめ
以上、コンポーネント内でstateを使用する際の注意点を3つ紹介しました。これらの注意点から言えることは、Reactにおけるstateは柔軟なので、取り扱いには注意が必要ということです。
Reactにおけるstateは、操作の順序やタイミングなどによって変化していくものです。変数に this.state を代入すれば、いつでも同じ値が取得できるわけではないので、どの時点のstateなのかを意識しながら取り扱う必要があります。
次回へ続きます。