プログラミング初心者のための入門ブログエンジニアが未来を切り開く。この広告は、90日以上更新していないブログに表示しています。React基礎Reactでは、関数コンポーネントでさまざまなライフサイクルを実現するためのフックがたくさん組み込まれておりますが、その中にuseMemoというフックがあります。Reactの初心者でなければ、useMemoおよびuseCallbackフックには既に馴染みがあるかと思います。useMemoおよびuseCallbackフックの使い道はいくつかの非常に特殊で具体的なケースに限られます。負荷の高い計算によってアプリのパフォーマンスが低下していることに気付く確率は10%も満たないでしょう。これらフックに関係なく、実行されるすべてのコード行にはコストがかかります。事前に最適化をしたいだけかもしれません。ほとんどの場合は私たち(開発者)はそれらを使って不必要なものをラップしています。useMemoとuseCallbackのこれらの実際の目的は何なのか、そしてそれらを適切に使用する方法と仕組みについて解説していきます。最後にメモ化の概念とは?ほとんどの場合、関数型プログラミング言語でメモ化に遭遇します。この手法には幅広い用途があります。JavaScriptでは、一部の関数はメモ化できないことに注意が必要です。プログラミングにおいて、メモ化はアプリケーションをより効率的に、そしてより高速にする最適化技術であり、計算結果をキャッシュに保存し、同じ情報が必要になったときに、再度計算する代わりにキャッシュから取り出すことで、高速化を実現するための技術です。メモ化とは、関数から返された値または関数自体を保存しておき、送信された値が変更されない場合は再度実行しないという考え方の概念となります。アルゴリズムについて精通している方であれば、メモ化は動的計画法(DP)の問題に対する一般的な戦略の1つです。複雑な問題をより単純な部分の問題に分解することによって解決する方法です。メモ化は、主にコンピュータープログラムを高速化するために使用される最適化手法となっています。useMemoフックとは?このフックは、Reactアプリケーションで値をメモ化するために使用します。Reactでのメモ化でも、計算された値をメモリに保存して再計算を回避する最適化手法です。この処理によって再計算が指定されるまでは計算が一度だけ実行されることになり、プログラムの実行が高速化されます。useMemoフックは、Reactコンポーネントの計算値をメモして、コンポーネントの再レンダリング時に再計算されないようにします。useCallbackフックとは?このフックはuseMemoと非常によく似ていますが、関数をキャッシュに保存します。useCallbackの方が少し簡単となります。いつ使用するべきなのか?キャッシュされたコンポーネントにPropsとして異なる関数を渡すと、不要な再レンダリングが発生することになります。useCallbackフックを使用すると、コンポーネントが初回レンダリングされたときだけ関数を作成します。コンポーネントが再レンダリングされるときには、キャッシュから関数を取得するだけで、今度は同じ関数になり、子コンポーネントでの再レンダリングはトリガーされません。単刀直入に言いますと、それは再レンダリング間のメモ書きとなります。値または関数がこれらのフックにラップされている場合、Reactは初回のレンダリング時にそれをキャッシュして、連続したレンダリング時に保存された値への参照を返します。このフックを使用しない場合、配列、オブジェクト、関数などの非プリミティブ値が再レンダリングのたびに最初から再作成されます。メモ化は、それらの値を比較するときに非常に役立ちます。Reactコンポーネントの一部の計算値は、更新される状態に依存するため状態が変化した際に、その値の再計算によって更新されたデータが取得されることが期待されます。一部の計算値は異なる状態に依存する場合があり、これはそのような値を常に再計算する必要がないことを意味しています。これらの2つのフックは、初心者には混乱を招く可能性があります。どちらも関数と依存関係の配列を引数として受け取り、依存関係の1つが変更された場合にのみ新しい値を計算します。この2つの違いは、useMemoは渡された関数の結果であるメモ化された値を返し、useCallbackはメモ化された関数そのものを返すことです。それらをいつ、どのように使用するかを見ていきましょう。このuseMemoフックは、useEffectフックとほぼ同じ挙動をします。]を渡さない場合、useMemoは更新時にトリガーされます。[]);ですのでフックを再度トリガーする場合では、その空の配列にいくつかの依存関係を追加します。]);上記の配列内のこれらの依存関係のいずれかの値が変更されると、useMemoは再トリガーされ、新しい値がメモ化された値として保存される事になります。下記の典型的なReactのユースケースを確認下さい。code...};sampleValueはuseEffectフックの依存値となっています。App内で定義されたオブジェクトなので、再レンダリングのたびにゼロから再作成されます。falseを返し、再レンダリングのたびにuseEffectがトリガーされていきます。これを避けるために、ここでuseMemoの出番という事です。useMemoフックでsampleValueの値をラップしてあげます。};useMemoの第一引数はコストのかかる操作を含む関数です。2番目の引数は前述した通り依存関係の配列となります。依存関係のいずれかの値が変更されると、Reactはキャッシュを削除し、コストのかかるタスクを実行します。する事によってuseEffectフックは、sampleValueの値が実際に変化したときだけ、トリガーされるようになります。これは、複雑な計算によって値を計算する必要がある場合または高コストのかかるデータベースおよびネットワークを呼び出す必要がある場合があります。度実行され、値がメモ化された値として保存されます、そして次回にローカル変数を参照すると、値がより速く取得されます。useCallbackフックも同様です、関数そのものをメモ化するのに便利なだけとなります。code...};他の例でも見てみましょう。CodePen.最初の引数としてコールバック関数、2番目の引数として依存関係を受け入れるuseCallbackを呼び出す必要があります。[counter]);依存関係の空の配列を渡すこともできます。その場合では関数は一度だけ実行されます、そうでない場合は、呼び出すたびに新しい値を返します。上記では、メモ化される関数と値が変更されるたびにその関数を更新する依存関係の配列の2つの値を受け取ります。重要なポイントは、useMemoおよびuseCallbackの両方は、再レンダリング時のフェーズのみ有効であることです。初回レンダリング時に役に立たないだけでなく、有害でもあります。初回レンダリング時に、アプリの動作がわずかですが遅くなります。アプリケーションのいたるところに多くものレンダリングがある場合、この速度低下は測定可能なレベルにまで達することさえあります。再レンダリングを防ぐためのPropsのメモ化useCallbackとuseMemoが本当に役立つことの1つとして最も重要で頻繁に使用されるのは、子コンポーネントに値を渡してProps値をメモ化することです。子コンポーネントに値を渡す場合です。);}Childコンポーネントは下記のようにします。CodePen.子コンポーネントの再レンダリングのトリガーは、親コンポーネントが再レンダリングするたびに新しいonClick関数が生成されて子コンポーネントに送信されます。onClick関数は再レンダリングのたびに生成されるので、ChildではonClickの参照が更新されたことを検知し、Propsの基本的な比較のみによってChildコンポーネントを再レンダリングします。useCallbackフックは、親コンポーネントがレンダリングされるたびにメソッドが再作成されるのを防ぐのに役立ちます。[]);useCallbackで指定された依存関係が更新されない場合、最初の引数として提供されたコールバックのメモ化された結果が返されます。Appコンポーネントの再レンダリングのたびにonClickが生成されることはなく、onClickのインスタンスがメモ化されてChildに渡されることになります。);}コンソールの出力はこちらのcodepenでご確認下さい。propsの依存関係が同じである限り、同じ関数のインスタンスを返します。useMemoフックを使用しメモ化された関数をテストし、子コンポーネントに値を渡します。下記のAppコンポーネントでは、Propsが同じでもbuttonをクリックするたびにexpensive()関数が呼び出されています。回だけ呼び出し、expensive()の、その後の呼び出しのためにメモリに格納された値を返します。useMemoフックの依存関係は通常では、親コンポーネントから渡されたPropsに由来しています。コンポーネントに渡されたPropsが同じままである限り、メモ化された関数はスキップされます。メモ化された結果がフックによって返されています。値がメモ化されていない場合、ページは再レンダリングされていきます。コンポーネントのPropsをメモ化する、それ以外で渡した場合ではすべてメモリの無駄使いであり、コードを不必要に複雑にします。それが、useCallbackでラップされているかどうかは関係ありません。下記ではhandleClickがメモ化されているかどうかに関係なく、ページは再レンダリングされます。/>親コンポーネントは、子を見つけ再レンダリングしてしまいます。これらフックの使用で意識しておかなければいけない事は、PropsのuseCallbackとuseMemoは、それだけでは再レンダリングを防げません。すべてのPropsとコンポーネント自体がメモ化されて初めて、再レンダリングが防げます。一つの間違いで、すべてが崩れてしまい、これらのフックは役に立たなくなります。見つけたら削除するように心掛けて下さい。一部のReact開発者がよく行う間違いは、パフォーマンスの問題を防ごうとして、必要のないときにもこれらのフックを乱用してしまう事です。それによってコードがより複雑になり、場合によってはパフォーマンスが低下することさえあるので、お勧めできません。これは、パフォーマンスの問題を発見した後に適用すべきものです。Dodds氏も以前にこれらフックの使用について、下記のように述べていました。パフォーマンスの最適化は無料ではありません。それらには常にコストが伴いますが、そのコストを相殺するメリットが常にもたらされるとは限りません。したがって、責任を持って最適化する必要があります。useCallbackとuseMemoを使用する場合は、コンポーネントが複雑になることに注意してください。思い通りに動作しないときは、ボトルネックがどこにあるのかをチェックし、その部分を最適化するように心掛けて下さい。コードの複雑さが増すだけではございません、useCallbackおよびuseMemoフックが役立つのと同様に、このフックがこれらの値を格納するためにメモリの一部を使用していることも、あなたは理解する必要があります。メモ化された関数が多ければ多いほど、より多くのメモリが必要になります。これらのコールバックを使用するたびに、React内部にはキャッシュされた出力を提供するためにさらに多くのメモリを使用する必要があるコードが多数存在します。メモ化されていない他の関数では、Reactによって破棄され、メモリが解放されます。useCallbackおよびuseMemoフックはパフォーマンスの向上に役立ちますが、落とし穴もあるという事です。パフォーマンス上の利点より欠点が上回る可能性があることを念頭に入れといて下さい。最後にほとんどの場合では、これらの最適化を使用する必要はありません。すべてにコストがかかり、その場合によりコードが複雑になります。それが価値があるかどうかを判断するのはあなた次第となります。これらフックはReactが提供する優れた機能の一部です。Reactプロジェクトで最高のパフォーマンスとレンダリング時間の速度を確保するために、特定の使用ケースをすべて考慮する必要があります。本日は以上となります。最後まで読んで頂きありがとうございます。17:56ご訪問いただきありがとうございます。私はdeve.Kと申します。現役のReact.jsプログラマーでありWebアプリ開発に携わっています。当ブログでは、ITやプログラミングの学習を目的としたブログを運営しています。専門分野はJS/TS/Node.js/React/Next.jsです。質問や相談、または学びたい分野のリクエストなどがありましたら、お気軽にお問い合わせください。このブログでは、ITやプログラミングに関する情報を分かりやすく、かつ実践的な内容でお届けしています。皆様にとって有益な情報を提供し、より良い学習体験を得ていただけるよう、精一杯の努力をしてまいります。今後ともどうぞよろしくお願いいたします。[deve.K].はてなブログをはじめよう!dev04Kさんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。