Motomichi Works Blog

モトミチワークスブログです。その日学習したことについて書いている日記みたいなものです。

React.memoで子コンポーネントの再描画を抑制する

関数コンポーネントの再描画とは

関数が実行されて、HTML文書構造をreturnすることで、対象のDOMが再描画されます。

関数コンポーネントが再描画(再実行)されるタイミング

以下のようなときに再描画(再実行)されます。

  1. stateが変化した時
  2. propsが変化した時
  3. コンポーネントが再描画された時

React.memoについて

React.memoを使うと関数コンポーネントをメモ化することができ、上記した3の場合について、無駄な再描画を減らすことができます。

サンプルコード

例えば以下のようなParent.tsxHoge.tsx、Foo.tsxを作成します。

Hoge.tsxとFoo.tsxはメモ化したうえで、子コンポーネントとして使用します。

ボタンをクリックして親コンポーネントで定義したstateを変化させ、これら3つのコンポーネントの再描画の挙動について確認していきます。

Parent.tsx

import React from 'react';
import { Hoge } from '@/components/common/elements/Hoge';
import { Foo } from '@/components/common/elements/Foo';

export function Parent() {
  const [hoge, setHoge] = React.useState(0);
  const [foo, setFoo] = React.useState(0);

  function incrementHoge() {
    setHoge(hoge + 1);
  }

  function incrementFoo() {
    setFoo(foo + 1);
  }

  console.log('Parentを再実行します。');

  return (
    <>
      <button onClick={incrementHoge}>hoge + 1</button>
      <button onClick={incrementFoo}>foo + 1</button>
      <Hoge hoge={hoge} />
      <Foo foo={foo} />
    </>
  );
}

Hoge.tsx

import React from 'react';

type Props = {
  hoge: number;
};

export const Hoge = React.memo(function Tmp({ hoge }: Props) {
  console.log('Hogeを再実行します。');

  return <div>{hoge}</div>;
});

Foo.tsx

import React from 'react';

type Props = {
  foo: number;
};

export const Foo = React.memo(function Tmp({ foo }: Props) {
  console.log('Fooを再実行します。');

  return <div>{foo}</div>;
});

コードの解説

コンポーネントをメモ化していない場合、 hoge + 1 ボタンをクリックすると、実行結果に変化がないFoo.tsxまで再実行されてしまいます。

これは上記した「関数コンポーネントが再描画(再実行)されるタイミング」の「3. 親コンポーネントが再描画された時」に該当するためです。

このサンプルコードのように子コンポーネントをメモ化することで、 hoge + 1 ボタンをクリックしたときに、実行結果に変化がないFoo.tsxは再実行されないことがわかります。

同様に foo + 1 ボタンをクリックした場合には、実行結果に変化がないHoge.tsxは再実行されないことがわかります。