React の memo API

次のフックについて説明する前に、 コンポーネントの props が変更されない場合の 再レンダリングを防ぐのに役立つ便利な API memoについても触れておく必要があります。

例を使って見ていきましょう。 2つの入力フィールドを持つ コンポーネント App を作成します。 ここでは名前と名字を入力するものとします:

App.jsxreturn (
  <div>
    <label>
      name:
      <input value={name} onChange={(e) => setName(e.target.value)} />
    </label>
    <br />
    <label>
      surname:
      <input value={surn} onChange={(e) => setSurn(e.target.value)} />
    </label>
  </div>
);

コンポーネントの先頭に、 それぞれの状態を追加しましょう:

App.jsxconst [name, setName] = useState('');
const [surn, setSurn] = useState('');

さらに、子コンポーネント Child.js を作成しましょう。 入力された名前を表示する挨拶文を出力します。 また、このコンポーネントがレンダリングされるたびに、 コンソールに文字列 'child render' が 出力されるようにします:

Child.jsxfunction Child({ name }) {
  console.log('child render');
  return <h3>Hello {name}!</h3>;
}

メインコンポーネントのマークアップ内の最後の入力フィールドの後に Child を追加し、 プロップとして名前を渡します:

App.jsx<Child name={name} />

メインコンポーネントにインポートします:

App.jsximport Child from './Child';

では、コンソールを開き、 入力フィールドに名前と名字を入力してみましょう。 ここで、名字のフィールドに文字を入力した場合でも、 子コンポーネントが毎回再描画されることがわかります。 コンポーネントが小規模であれば特に問題はありません。 しかし、このコンポーネントが大量のデータを表示し、 かつ毎回再描画されるとしたらどうでしょうか。

では、子コンポーネントを memo でラップし、 レンダリングがどのように変わるか見てみましょう。 まず、子コンポーネントに memo をインポートします:

Child.jsximport { memo } from 'react';

次に、新しい変数を作成し、 memo でラップされた Child を 代入します。次のような 関数式が出来上がります:

Child.jsxconst Child = memo( function Child({ name }) {
  console.log('child render');
  return <h3>Hello {name}!</h3>;
});

コンソールを開き、もう一度 フィールドに名前と名字を入力してみましょう。 今度は、名字を入力しても、 子コンポーネントが再描画されないことがわかります。

ただし、これは props が変更されなくても、 コンポーネントが使用している状態が変更されたり、 変更を使用するコンテキストがある場合は 機能しないことに注意する必要があります。

Задание 1

このレッスンで作成した App コンポーネントのコードを基に、 最初の入力フィールドのみを残してください。 テキスト 'long text' を含む段落を持つ React コンポーネント Text を作成し、 それを入力フィールドの後に App 内に配置してください。

Задание 2

Text コンポーネントに console.log('text render'); の行を追加してください。 入力フィールドに文字を入力するたびに、 Text コンポーネントが毎回再描画されることを確認してください。

Задание 3

次に、Text コンポーネントを memo でラップしてください。 入力フィールドに文字を入力しても、 Text コンポーネントが再描画されないことを確認してください。