【マ？】あなたが知らないかもしれないReactのホント【メモ化編】

みなさん、おはようございます:runner:！
@f0lstです！

この記事は<a href="https://qiita.com/advent-calendar/2023/ateam-life-design">Ateam LifeDesign Advent Calendar 2023</a> の 15日目の記事になります :santa: 

## :point_up: はじめに

**全国47都道府県**のReactを学ぶ紳士淑女の皆さん！
毎日、自己研鑽おつかれさまです :santa_tone1: 

やあああ、スキルアップって楽しいですね :relaxed:(唐突)

## :smiling_imp: あなたが知らないかもしれないReactのホント

「**あなたが知らないかもしれないReactのホント**」とは大きく出てしまいました :baby_tone1:

紹介させていただくのは、**公式ドキュメントからの抜粋**です。
詳しくみたい方は是非公式も見に行ってください :see_no_evil: 

記念すべき**第一回**は**メモ化**についてお話ししていきます :sparkles: 

## :angel_tone1: そのメモ化って本当に必要？

皆さん、`memo`や`useMemo`などは活用していますか？

`memo`は、**propsが変更されていない場合レンダーをスキップ**してくれます。
`useMemo`は、**計算結果をキャッシュ**してくれますね。

どちらも有用で、**パフォーマンス向上には必要**なことです。

...
...
...

**果たして、本当にそうでしょうか？**

### :no_good_tone1: 大抵の場合はメモ化は不要である

ここでReactの公式ドキュメントを見てみましょう :thinking:

以下はドキュメントからの引用です :point_down_tone1: :point_down_tone1:

> あなたのアプリがこのサイトのように、ほとんどの**インタラクションが大まかなもの**（ページ全体やセクション全体の置き換えなど）である場合、**メモ化は通常不要**です。<br />
一方、あなたのアプリが**描画エディタのようなもの**で、ほとんどの**インタラクションが細かなもの**（図形を移動させるなど）である場合、**メモ化は非常に役に立つ**でしょう。
<a href="https://ja.react.dev/reference/react/memo">memo – React</a>

と記載があります。

つまりは、インタラクションの細かい**描画エディタ**のようなものでない限り、**メモ化は不要**と言うことですね :head_bandage:

### :hugging: なんでもかんでもメモ化するんじゃないぞ

また、こんな記載もあります  :point_down_tone1: :point_down_tone1:

> 個別のケースを考えずに、**可能な限りすべてをメモ化**するようにしているチームもあります。
このアプローチの**デメリットは、コードが読みにくくなる**ことです。
<a href="https://ja.react.dev/reference/react/memo">memo – React</a>

**メリットがない**どころか、**コードの可読性を下げる**ことにも繋がると言うことですね :ghost:

**良かれと思って実装したメモ化**も、**メリットがなく、可読性も下がって**しまっては**本末転倒**です！

### :innocent: じゃあ、どうすればええん！？

「**じゃあ、何もしなくっていっか〜** :wink: 」となってはいけません :no_good_tone1: 

公式ドキュメントでは、**メモ化を使わず、パフォーマンスを向上**する手段も提供されています！

## :hand_splayed_tone1: メモ化を不要にする5つの手段

公式ドキュメントでは、**メモ化を不要にする5つの手段**が紹介されています。

では、一つずつ手段を見ていきましょう〜 :mag:

### 1. 子コンポーネントをchildrenとして受け入れる :boy_tone1:

まず1つ目は、「**子コンポーネントをchildrenとして受け入れる**」です。

以下はドキュメントからの引用です :point_down_tone1: :point_down_tone1:

> コンポーネントが他のコンポーネントを視覚的にラップするときは、それが**子としてJSXを受け入れる**ようにします。
これにより、ラッパコンポーネントが自身のstateを更新しても、Reactはその子を再レンダーする必要がないことを認識します。

ちょっとこの説明はわかりづらいですね :angry: 
実際のコードを見て確認しましょ〜！

#### :ok_woman_tone3: コード例
以下のコードは3つのコンポーネントで構成されています。

```React
const ChildComponent = () => {
  {/* 単なるテキストを持つ */}
  return <p>これはChildComponentです</p>;
};

const ParentComponent = ({ children }: Props) => {
  {/* `ChildComponent`を`children`として受け取っている */}
  {/* `count`のstateを持つ */}
  {/* `count`のstateを更新する`<button>`を持つ */}
  const [count, setCount] = useState(0);
  return (
    <div>
      {children}
      <p>{count}</p>
      <button onClick={() => setCount((prev) => prev + 1)}>Increment</button>
    </div>
  );
};

const SampleComponent = () => {
  {/* `ParentComponent`と`ChildComponent`を持つ */}
  return (
    <ParentComponent>
      <ChildComponent />
    </ParentComponent>
  );
}
```

では、**再レンダリングの観点**からこのコードを見ていきましょう :eyes:

注目すべきは、`ParentComponent`で`count`の**stateが更新**された時です。

流れは以下です :point_down_tone1: 

1. `<button>`がクリックされる
2. `onclick`に指定された`setCount((prev) => prev + 1)`が呼ばれる
3. セッター関数が呼ばれたことで`count`を使う`ParentComponent`が再レンダリングされる

では、`ParentComponent`が**再レンダリング**されたタイミングで、`ChildComponent`はどうなるでしょうか？

普通に考えれば、**親である`ParentComponent`が再レンダリング**されているため、同じタイミングで**子コンポーネントである`ChildComponent`も再レンダリングされる**と考えるかもしれません。

が、この場合は、`ChildComponent`は**再レンダリングされません** :ok_woman_tone1:

これが、引用で言われていた以下の部分です :point_down_tone1: 

> コンポーネントが他のコンポーネントを視覚的にラップするときは、それが**子としてJSXを受け入れる**ようにします。
これにより、ラッパコンポーネントが自身のstateを更新しても、Reactはその子を再レンダーする必要がないことを認識します。

つまり、**`ParentComponent`が`ChildComponent`をJSXとして受け取っている**ため、**ラッパーコンポーネントである`ParentComponent`でstateが更新**されても、**子コンポーネントである`ChildComponent`を再レンダリングしない**ということですね :sparkles: 

使える場面はある程度限定されますが、**Appの上位に近いコンポーネント**では有効活用できるテクニックだと思います！

ぜひ、頭の片隅に置いて開発を行っていきましょう :keyboard:

### 2. stateを必要以上にリフトアップしない :no_good_tone1:

2つ目は、「**必要以上にstateをリフトアップしない**」です。

同じくドキュメントからの引用です :point_down_tone1: :point_down_tone1:

> ローカルstateを優先し、必要以上に state のリフトアップを行わないようにします。
フォームや、アイテムがホバーされているかどうか、といった頻繁に変化するstateは、ツリーのトップやグローバルの状態ライブラリに保持しないでください。

**stateを必要以上にリフトアップしない**ことで、stateが更新された時の**再レンダリングを最低限**にすることができます :sparkles:

極力、stateは**使うコンポーネント内**、もしくは親コンポーネントで定義する場合でも、**必要以上に上位のコンポーネントで定義しない**ようにしましょう！

### 3. バグの回避のためにメモ化をしない :japanese_ogre:

3つ目は、「**バグの回避のためにメモ化をしない**」です。

同じくドキュメントからの引用です :point_down_tone1: :point_down_tone1:

> レンダーロジックを純粋に保ちます。
コンポーネントの再レンダーが問題を引き起こしたり、何らかの目に見える視覚的な結果を生じたりする場合、それはあなたのコンポーネントのバグです！
メモ化を追加するのではなく、バグを修正します。

要するに、**メモ化することでバグを回避するんじゃねぇよ**ってことですね :relaxed:
正論すぎて、**ぐう**の音も出ませんね :relaxed:
みなさん気をつけましょう :relaxed:

### 4. stateを更新する不要なエフェクトを避ける :runner:

4つ目は、「**stateを更新する不要なエフェクトを避ける**」です。

同じくドキュメントからの引用です :point_down_tone1: :point_down_tone1:

> stateを更新する不要なエフェクトを避けてください。
Reactアプリケーションのパフォーマンス問題の大部分は、エフェクト内での連鎖的なstate更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。

エフェクトの処理が**パフォーマンスに大いに影響を与える**ことが言及されていますね :head_bandage: 

stateを更新する上で`useEffect`を使用する場面があると思います。
が、そんな時は一度以下のドキュメントを読んでみてください！
もしかすると、**そのエフェクトは必要ない**かもしれません！

https://ja.react.dev/learn/you-might-not-need-an-effect

### 5. エフェクトから不要な依存値をできるだけ削除する :wastebasket:

5つ目は、「**エフェクトから不要な依存値をできるだけ削除する**」です。

同じくドキュメントからの引用です :point_down_tone1: :point_down_tone1:

> エフェクトから不要な依存値をできるだけ削除します。
例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。

ここでもエフェクトに関して言及されており、**エフェクトがいかにパフォーマンスに影響を与える**かがよくわかります。

エフェクトは**上手く使えれば強力な武器**になりますが、下手に使えば**パフォーマンスや可読性を悪化**させます。

これらについても、公式ドキュメントに記載がありますので、ぜひご覧ください〜 :cooking: 

https://ja.react.dev/learn/removing-effect-dependencies




## :santa_tone1:  まとめ

はい！まとめでございます！

今回は「**メモ化が本当に必要なのか**」という部分にフォーカスして、公式ドキュメントから引用してきました。

私個人としては、このドキュメントを読むまでは、**メモ化は基本すべきもの**だと思っていたので、とても驚きました :upside_down:

特に**メモ化を不要にする5つの手段**は、パフォーマンスに関する基本的な知識となり、普段の開発で有用に活用できる手段ばかりです！

このように公式ドキュメントには、開発のクオリティを上げる情報が詰め込まれています！
隅々まで読み込みましょう :book: 

公式ドキュメントを読むオススメの勉強法も記事にまとめましたので、ぜひご覧ください！

https://qiita.com/f0lst/items/da4f58a56986bb7094d6