Yanonoblog!

こつこつと

Reactコンポーネントの再レンダリングとmemo

はじめに

本記事では「モダンJavaScriptの基本から始める React実践の教科書」で学んだ内容と別途気になって調べた内容や知識も含めアウトプットしています。

書籍に記載されている内容を順序立ててまとめるというよりは、整理しておきたい周辺の知識と深ぼった内容を雑多に書いています。

Reactのレンダリングについてまとめています。

レンダリングが発生する条件

Reactコンポーネントの再レンダリングパターンは3つあります。

  1. Stateが更新されたコンポーネント
  2. Propsが変更されたコンポーネント
  3. レンダリングされたコンポーネントの子コンポーネント全て
Stateが更新されたら再レンダリングが発生する

Stateはコンポーネントの状態を表す変数であり、その状態が更新されたときに再レンダリングが発生することで画面の表示を正しく維持します。

コンポーネント内で管理されるStateの値が変更された場合、コンポーネントは再レンダリングされます。

Stateが新しいオブジェクトへの参照として変更された場合でも再レンダリングが発生します。

Propsが変更されたら再レンダリングが発生する

ReactコンポーネントはPropsを引数として受け取り、Propsに応じてレンダリング内容が決まるため、Propsの値が変わったときは再レンダリングしてコンポーネントが更新される必要があります。

レンダリングされたコンポーネントの子コンポーネント全てに対して再レンダリングが発生する

コンポーネントが再レンダリングされた場合、そのコンポーネントが直接的に子コンポーネントとしてレンダリングしているコンポーネントのみが再レンダリングされます。

コンポーネントは特にPropsが変更されていなくても親が再レンダリングされたら再レンダリングされるため、場合によっては表示が変わらないのに無駄なサイレンアリングを行うことでパフォーマンスが低下することになってしまうため、最適化する必要があります。

不要な再レンダリングを避ける方法

React.memoを使う

Reactコンポーネントをメモ化するための高階コンポーネント(Higher-Order Component)です。

メモ化されたコンポーネントは、Propsが変更されない限り再レンダリングされず、以前の結果が再利用されます。

これにより、不要なレンダリングをスキップしてパフォーマンスを最適化することができます。

React.memoは、コンポーネントをラップする形で使用します。

const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});

React.memoによってメモ化されたコンポーネントは、Propsが変更されない限り再レンダリングされません

同じPropsでの再レンダリングでは以前の結果が再利用されるため、不要なレンダリングがスキップされます。

React.memo は props の変更のみをチェックします。 React.memo でラップしているコンポーネントがその実装内で useState、useReducer や useContext フックを使っている場合、state やコンテクストの変化に応じた再レンダーは発生します。

React.memoは以下のようなコンポーネントの再レンダリングをスキップすることで、パフォーマンスの向上が期待できます。

useMemoを使う

レンダリングのたびに同じ結果が得られる関数や式を最適化する場合や、プロパティやステートの値に基づいて計算結果をキャッシュする場合はuseMemoを使用すると良いです。

負荷の高い演算はラップして結果をキャッシュに持つ手法のことを「メモ化」と呼びます。

下記が子コンポーネントだとするとcountの値が変わっていなければ再計算が行われることはありません。

import React, { useState, useMemo } from "react";
import "./styles.css";

export default function App() {
  const [count, setCount] = useState(0);

  // countの値に基づいて計算結果をメモ化
  const calculatedResult = useMemo(() => {
    console.log("Calculating result...");
    // ここでは単純にcountの2倍を計算していますが、実際のアプリケーションではより複雑な計算が行われることが想定されます
    return count * 2;
  }, [count]);

  const handleClick = () => {
    setCount((prevCount) => prevCount + 1);
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <p>Count: {count}</p>
      <p>Calculated Result: {calculatedResult}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

ただし、useMemo のキャッシュを参照するオーバーヘッドは、実際の値を再計算するよりもコストがかかるため、計算コストの高い関数や式の結果を再利用する場合にのみ有用です。

参考

続く…

コメント

本記事の内容は以上になります!

書籍の続きのアウトプットも随時更新したいと思います。


プログラミングスクールのご紹介 (卒業生より)

お世話になったプログラミングスクールであるRUNTEQです♪

https://runteq.jp/r/ohtFwbjW

こちらのリンクを経由すると1万円引きになります。

RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。

もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。

https://twitter.com/outputky