0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

React メモ化(React.memo())を利用して再レンダリングを制御する

Last updated at Posted at 2023-04-02

メモ化

Reactはstatepropsが更新される度に再レンダリングが行われます。
useCallback()React.memo()useMemo()を利用すると、初回の処理を実行して記録し、値が必要となった2回目以降は、保持していた計算結果を再利用します。これをメモ化といいます。

メモ化は再レンダリングする必要がなくなるため、パフォーマンスの向上を期待できます。

React.memo()

React.memo()はコンポーネントのレンダリング結果をメモ化するReact APIです。親コンポーネントから渡されたpropsについて前回との変更差分をチェックし、コンポーネントが返した描画結果を記録してメモ化し、差分があった場合のみ再レンダリングを行います。

  1. 前回のpropsと新しく親コンポーネントから渡されたpropsを比較し、値が等価であるかチェックする
  2. 等価である場合、メモ化したコンポーネントを利用するため、再レンダリングをスキップする
  3. 等価でない場合、再レンダリングを行う

また、React.memo()でコンポーネントをラップしても、ラップされたコンポーネント内でuseSate()useContext()を利用している場合、その変化に応じたコンポーネントの再レンダリングが行われます。頻繁に再レンダリングされる親コンポーネントを持つ子コンポーネント以外で利用しても大きなパフォーマンス効果を得ることはできません。

React.memo()を使用したサンプル

React.memo()を使用した成果物を作成しました。
再レンダリングが行われる度に、Consoleに変更したラジオボタンリストのメッセージが表示されます。
例えば、ラジオボタンリストAの値を変更した場合、Consoleに「ラジオボタンリストAが選択されました」と出力されます。この時、ラジオボタンリストBの値は変更されていないため、Consoleには出力されません。

コード解説

index.js

2回レンダリングされるのを防ぐためにStrictModeは削除しました。

import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(<App />);

App.js

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

// ラジオボタン用の要素を持った配列を設定
const items = [
  { id: 1, item: "東京" },
  { id: 2, item: "大阪" },
  { id: 3, item: "愛知" },
  { id: 4, item: "福岡" },
  { id: 5, item: "北海道" },
  { id: 6, item: "宮城" }
];

// 親コンポーネントRadioからprops(name, handleChange, value)を受け取る
const RadioOption = (props) =>

  // map()を用いて配列itemsの要素を1つひとつ設定する
  items.map((item) => {
    return (

      // ラジオボタン内で一意となるkey属性を設定
      <label key={item.id}>
        <input
          type="radio"
          value={item.item}

          // ラジオボタンリストA・Bと2つ存在するため、リストを分けるのにnameを設定する
          name={props.name}

          // ラジオボタンで選択された値を親コンポーネントで定義されている状態変数valueA,valueBに渡す
          onChange={props.handleChange}

          // ラジオボタンの初期値(最初にチェックされている値)を設定する
          // 親コンポーネントから渡されるvalueには初期値にitems[0].item=東京が設定されている
          // そのため、最初にラジオボタンにチェックされているのは東京になる
          checked={item.item === props.value}
        />
        {item.item}
      </label>
    );
  });

// 親コンポーネントRadioからprops(text, value)が渡される
// React.memoでRadioResultコンポーネントをラップする
// 親コンポーネントRadioから渡されたprops(text, value)を記録する
// 2回目以降は前回のprops(text, value)と比較し、等価でない場合のみ再レンダリングを行う
const RadioResult = React.memo((props) => {
  console.log(`${props.text}が選択されました`);
  return (
    <p>
      選択された値:<b>{props.value}</b>
    </p>
  );
});

const Radio = () => {

  // ラジオボタンリストAで現在選択されている値を設定する状態変数valueA
  // valueAを更新するsetValueA
  const [valueA, setValueA] = useState(items[0].item);

  // ラジオボタンリストBで現在選択されている値を設定する状態変数valueB
  // valueBを更新するsetValueB
  const [valueB, setValueB] = useState(items[0].item);

  // ラジオボタンリストAで選択された値(value)を設定する関数handleChangeAを定義
  const handleChangeA = (e) => setValueA(e.target.value);

  // ラジオボタンリストBで選択された値(value)を設定する関数handleChangeBを定義
  const handleChangeB = (e) => setValueB(e.target.value);

  return (
    <>
      <div className="radio-list">
        <div>
          ラジオボタンリストA
          <RadioOption
            name="radioA"
            value={valueA}
            handleChange={handleChangeA}
          />
          <RadioResult text="ラジオボタンリストA" value={valueA} />
        </div>
        <div>
          ラジオボタンリストB
          <RadioOption
            name="radioB"
            value={valueB}
            handleChange={handleChangeB}
          />
          <RadioResult text="ラジオボタンリストB" value={valueB} />
        </div>
      </div>
    </>
  );
};

export default function App() {
  return <Radio />;
}

今回の処理は親コンポーネントであるRadioからReact.memo()でラップされた子コンポーネントRadioResultprops(text, value)を渡す。
textは固定の文字列のため、変更される値はvalueのみである。
今回のプログラムの構造は以下のようになっていますが、RadioコンポーネントからRadioResultコンポーネントへ渡されるvalueAの値が変更されても、もう1つのRadioResultコンポーネントへ渡されるvalueBの値は変更されないため、ボタンリストAのみ再レンダリングされ、ンソール画面には「ボタンリストAが変更されました」と出力されます。

しかし、React.memo()を外した場合、親コンポーネントから渡されるvalueが変更されると、例えボタンリストAのみ変更しても、ボタンリストBを再レンダリングされ、コンソール画面には「ボタンリストAが変更されました」、「ボタンリストBが変更されました」と両方出力されます。

スクリーンショット 2023-04-02 13.05.01.png

styles.css

label {
  display: block;
}

.radio-list {
  display: flex;
  justify-content: space-around;
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?