ReactのuseCallbackフックを飼いならす方法を学ぶ

公開: 2022-04-27

React.jsが近年広く普及していることは周知の事実です。 これは、FacebookやWhatsAppなど、インターネットで最も著名なプレーヤーの多くが選択するJavaScriptライブラリになりました。

その上昇の主な理由の1つは、バージョン16.8でのフックの導入でした。 Reactフックを使用すると、クラスコンポーネントを記述せずにReact機能を利用できます。 現在、フックを備えた機能コンポーネントは、Reactを操作するための開発者の頼りになる構造になっています。

このブログ投稿では、メモ化と呼ばれる関数型プログラミングの基本的な部分に触れているため、1つの特定のフックであるuseCallbackについて詳しく説明します。 useCallbackフックをいつどのように利用し、パフォーマンス向上機能を最大限に活用するかを正確に知ることができます。

準備? 飛び込みましょう!


メモ化とは何ですか?

メモ化とは、複雑な関数がその出力を保存するときなので、次に同じ入力で呼び出されたときです。 キャッシュに似ていますが、よりローカルなレベルです。 複雑な計算をスキップして、すでに計算されているため、出力をより速く返すことができます。

これは、メモリの割り当てとパフォーマンスに大きな影響を与える可能性があり、その負担は、 useCallbackフックが軽減することを目的としています。

ReactのuseCallbackとuseMemo

この時点で、 useMemo useCallback呼ばれる別のフックとうまくペアになっていることを言及する価値があります。 両方について説明しますが、この記事では、メイントピックとしてuseCallbackに焦点を当てます。

主な違いは、 useMemoはメモ化された値を返すのに対し、 useCallbackはメモ化された関数を返すことです。 つまり、 useMemoは計算値を格納するために使用され、 useCallbackは後で呼び出すことができる関数を返します。

これらのフックは、依存関係(stateやpropsなど)の1つが変更されない限り、キャッシュされたバージョンを返します。

実際の2つの関数を見てみましょう。

 import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])

上記のコードスニペットは不自然な例ですが、2つのコールバックの違いを示しています。

  1. memoizedValueは配列[1, 2, 3, 4, 6, 9]になります。 値変数が残っている限り、 memoizedValueも残り、再計算されることはありません。
  2. memoizedFunctionは、配列[1, 2, 3, 4, 6, 9] 1、2、3、4、6、9]を返す関数になります。

これらの2つのコールバックの優れている点は、依存関係の配列が変更されるまでキャッシュされてハングアップすることです。 これは、レンダリング時にガベージコレクションが行われないことを意味します。

React.jsは、FacebookやWhatsAppなど、インターネットの最大のプレーヤーの多くが選択するJavaScriptライブラリになりました。 このガイドで詳細をご覧ください️ クリックしてツイート

レンダリングと反応

Reactに関して、メモ化が重要なのはなぜですか?

これは、Reactがコンポーネントをレンダリングする方法と関係があります。 Reactは、メモリに保存されている仮想DOMを使用してデータを比較し、何を更新するかを決定します。

仮想DOMは、パフォーマンスに対応し、アプリケーションを高速に保つのに役立ちます。 デフォルトでは、コンポーネントの値が変更されると、コンポーネント全体が再レンダリングされます。 これにより、Reactはユーザー入力に対して「リアクティブ」になり、ページをリロードせずに画面を更新できるようになります。

変更はそのコンポーネントに影響を与えないため、コンポーネントをレンダリングする必要はありません。 ここで、 useCallbackuseMemoによるメモ化が役立ちます。

Reactがコンポーネントを再レンダリングすると、コンポーネント内で宣言した関数も再作成されます。

関数の同等性を別の関数と比較する場合、それらは常にfalseになることに注意してください。 関数もオブジェクトであるため、それ自体と等しくなるだけです。

// these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true

つまり、Reactがコンポーネントを再レンダリングすると、コンポーネントで宣言されているすべての関数が新しい関数であると見なされます。

これはほとんどの場合問題なく、単純な関数は計算が簡単で、パフォーマンスに影響を与えません。 ただし、関数を新しい関数として表示したくない場合は、 useCallbackを使用して支援することができます。

「関数を新しい関数として見られたくないのはいつですか?」とお考えかもしれません。 ええと、 useCallbackがより理にかなっている特定のケースがあります:

  1. 関数をメモ化されている別のコンポーネントに渡しています( useMemo )。
  2. 関数には、覚えておく必要のある内部状態があります
  3. あなたの関数は、たとえばuseEffectのような別のフックの依存関係です

ReactuseCallbackのパフォーマンス上の利点

useCallbackを適切に使用すると、アプリケーションを高速化し、必要のない場合にコンポーネントが再レンダリングされるのを防ぐことができます。

たとえば、大量のデータをフェッチし、そのデータを次のようにグラフまたはグラフの形式で表示するコンポーネントがあるとします。

PHP、MySQL、Reddis、および外部(その他)の全体的なトランザクション時間をミリ秒単位で比較するカラフルな棒グラフ。
Reactコンポーネントを使用して生成された棒グラフ。

データ視覚化のコンポーネントの親コンポーネントが再レンダリングされたが、変更された小道具や状態はそのコンポーネントに影響を与えないとします。 その場合、おそらくそれを再レンダリングしてすべてのデータを再フェッチする必要はありません。 この再レンダリングと再フェッチを回避すると、ユーザーの帯域幅を節約し、よりスムーズなユーザーエクスペリエンスを提供できます。

ダウンタイムとWordPressの問題に苦しんでいますか? Kinstaはあなたの時間を節約するために設計されたホスティングソリューションです! 私たちの機能をチェックしてください

ReactuseCallbackの欠点

このフックはパフォーマンスの向上に役立ちますが、落とし穴もあります。 useCallback (およびuseMemo )を使用する前に考慮すべき点は次のとおりです。

  • ガベージコレクション:まだメモ化されていない他の関数は、メモリを解放するためにReactによって破棄されます。
  • メモリ割り当て:ガベージコレクションと同様に、メモ化された関数が多いほど、必要なメモリも多くなります。 さらに、これらのコールバックを使用するたびに、React内に大量のコードがあり、キャッシュされた出力を提供するためにさらに多くのメモリを使用する必要があります。
  • コードの複雑さ:これらのフックで関数をラップし始めると、すぐにコードの複雑さが増します。 これらのフックが使用されている理由をより深く理解し、正しく使用されていることを確認する必要があります。

上記の落とし穴に注意することで、自分でそれらに遭遇するという頭痛の種を減らすことができます。 useCallbackの採用を検討するときは、パフォーマンス上の利点が欠点を上回っていることを確認してください。

useCallbackの例を反応させる

以下は、ButtonコンポーネントとCounterコンポーネントを使用した簡単なセットアップです。 Counterには2つの状態があり、それぞれがCounterコンポーネントの状態の個別の部分を更新する2つのButtonコンポーネントをレンダリングします。

Buttonコンポーネントは、 handleClickとnameの2つの小道具を取ります。 ボタンがレンダリングされるたびに、コンソールにログが記録されます。

 import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }

この例では、いずれかのボタンをクリックするたびに、コンソールに次のように表示されます。

 // counter rendered // button1 rendered // button2 rendered

ここで、 useCallbackhandleClick関数に適用し、ボタンをReact.memoでラップすると、 useCallbackが提供するものを確認できます。 React.memouseMemoに似ており、コンポーネントをメモ化することができます。

 import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }

これで、いずれかのボタンをクリックすると、コンソールにログインするためにクリックしたボタンのみが表示されます。

 // counter rendered // button1 rendered // counter rendered // button2 rendered

ボタンコンポーネントにメモ化を適用しました。ボタンコンポーネントに渡されるprop値は等しいと見なされます。 2つのhandleClick関数はキャッシュされ、依存関係配列の項目の値が変更されるまで(たとえば、 countOnecountTwo )、Reactによって同じ関数と見なされます。

useCallbackフックをいつどのように利用するか、およびそのパフォーマンス向上機能を最大限に活用する方法を正確に学ぶ準備はできていますか? ここから始めましょう! クリックしてツイート

概要

useCallbackuseMemoはクールですが、特定のユースケースがあることを忘れないでください。すべての関数をこれらのフックでラップするべきではありません。 関数の計算が複雑な場合は、メモ化されたコンポーネントに渡される別のフックまたは小道具の依存関係が、 useCallbackのために到達したい可能性のある優れた指標です。

この記事が、この高度なReact機能を理解し、その過程で関数型プログラミングに自信を持てるようになることを願っています。