【React学習】〜Stateとレンダリング

React

 Reactの難しいところは、一言でいうと「レンダリング」だと思います。

 詳しい仕組みを理解しているわけではありませんが、Reactでは不要な処理を削減し、変更があった場所だけを表示し直すようにしています。
 コンポーネントを組み合わせてできたページを最初に表示する作業を「レンダリング」といい、変更があった場所を更新して再表示する作業を「再レンダリング」といいます。変更があったコンポーネントだけを再レンダリングすることで、高速な処理を実現しているのだそうです。

レンダリングとは

 「レンダリング」というのは、関数(=コンポーネント)を実行するということです。

const Example = () => {
  let a = 1;
  let b = 2;
  const increment = () => {
    a = a + 1;
  }
  return (
    <div>
      <h1>{ a+b }</h1>
      <button onClick={ increment }>+</button>
    </div>
  );
}

 関数コンポーネントの中では、HTML要素を返す前に、いろいろな処理を記述することができます。上のコードでは変数aとbに値を代入して、それを足したものをh1要素として表示するようにしています。ここでは「3」が表示されます。(ちなみにreturnの中の{ }はJavaScriptの「式」を記述できます。(「式」と「文」の違いもここで初めて学びました)

 buttonにはクリックしたときにincrementという処理を行うように関数を渡しています。ボタンをクリックするとincrementが実行され、aの値が1増えるので、h1に表示されるのは「4」だと期待されます。しかし、残念ながらそうはなりません。

 これは「レンダリング」と関係します。

 試しにincrementの中にconsole.log(a)を追加してみると、確かにクリックするたびにaの値は1ずつ増えていきます。でも表示された値は変わりません。

 つまり「再レンダリングされていない」ということです。

再レンダリングのきっかけ

 Reactが再レンダリングするきっかけは、3つあります。

  1. stateが更新されたとき
  2. propsが更新されたとき
  3. 親コンポーネントが再レンダリングされたとき

 今回の例では、コンポーネントを再レンダリングさせるには「state」というものを更新する必要があるということです。この「state」がReactを難しくしている一因なのではないでしょうか。

useState

 コンポーネントにstate(状態?)を持たせるための関数がuseStateです。(他にもuseから始まる関数がいくつもあって、まとめてReact Hooksと呼ばれます)useStateはコンポーネントの最初の方で以下のように宣言します。

const [ number, setNumber ] = useState(1);

 左辺の配列は「分割代入」というJavaScriptの記法です(この書き方もReactを学んでから覚えました)。useStateは引数を初期値とする「値」と、その値を更新するための「セッター」を配列の形で返す関数です。上の式は、useStateが返す値をnumberという変数に、セッターをsetNumberという変数に代入します。そして、

setNumber( prev => prev + 1 );

 このようにsetNumberに「新しい値」を渡します。すると、Reactは「新しい値」を元に再レンダリングしてくれます。ちなみに、渡しているのはコールバック関数で、

prev => prev + 1

function (prev) {
  return prev + 1;
}

と同じ意味です。(この記法はとても便利ですね)セッターには値を直接渡すこともできます。

セッターの使用上の注意

 このセッターを使うにも注意が必要で

setNumber( number + 1);
setNumber( number + 1);

 このように書くと意図した挙動となりません。一見setNumberを2回呼び出しているのでnumberの値は+2となりそうですが、結果は+1となります。これは、numberの値が更新されるのは再レンダリングのときだからです。レンダリング前のnumberの値を引数として渡しているので、結局同じ値を2回セットしたことになってしまうのです。

setNumber( prev => prev + 1 );
setNumber( prev => prev + 1 );

 このように書けば、prev(更新前の値)+1を新しい値として更新し、さらにprev(1回更新後の値)+1を新しい値として更新しますので、期待通り+2されます。

 こんな感じで、単に+1したいだけなのに、処理がいろいろと必要なわけです。さらに計算結果が表示されるかは「レンダリング」にかかっているので、いつレンダリングされるのかを把握していないと、期待通りの表示になりません。これがReactの難しいところではないかなと思います。

コメント