LoginSignup
7
1

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

Last updated at Posted at 2023-12-14

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

この記事はAteam LifeDesign Advent Calendar 2023 の 15日目の記事になります :santa:

:point_up: はじめに

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

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

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

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

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

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

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

皆さん、memouseMemoなどは活用していますか?

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

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

...
...
...

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

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

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

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

あなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの(ページ全体やセクション全体の置き換えなど)である場合、メモ化は通常不要です。

一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かなもの(図形を移動させるなど)である場合、メモ化は非常に役に立つでしょう。
memo – React

と記載があります。

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

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

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

個別のケースを考えずに、可能な限りすべてをメモ化するようにしているチームもあります。
このアプローチのデメリットは、コードが読みにくくなることです。
memo – React

メリットがないどころか、コードの可読性を下げることにも繋がると言うことですね :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つのコンポーネントで構成されています。

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:

注目すべきは、ParentComponentcountstateが更新された時です。

流れは以下です :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はその子を再レンダーする必要がないことを認識します。

つまり、ParentComponentChildComponentを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を使用する場面があると思います。
が、そんな時は一度以下のドキュメントを読んでみてください!
もしかすると、そのエフェクトは必要ないかもしれません!

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

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

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

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

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

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

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

:santa_tone1: まとめ

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

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

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

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

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

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

7
1
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
7
1