[解決済み] useCallbackとuseMemoは実際どう違うの?
質問
もしかしたら何か勘違いしているかもしれませんが、useCallback Hookは再レンダリングが発生すると毎回実行されます。
私は、useCallbackの第2引数として、不変の定数であるinputsを渡しましたが、返されたメモ化コールバックは、レンダリングのたびに私の高価な計算を実行します(私は確信しています。)
useCallbackをuseMemoに変更しました。useMemoは期待通りに動作し、渡された入力が変更されると実行されます。そして、本当に高価な計算をメモしています。
実例です。
'use strict';
const { useState, useCallback, useMemo } = React;
const neverChange = 'I never change';
const oneSecond = 1000;
function App() {
const [second, setSecond] = useState(0);
// This ???? expensive function executes everytime when render happens:
const calcCallback = useCallback(() => expensiveCalc('useCallback'), [neverChange]);
const computedCallback = calcCallback();
// This ???? executes once
const computedMemo = useMemo(() => expensiveCalc('useMemo'), [neverChange]);
setTimeout(() => setSecond(second + 1), oneSecond);
return `
useCallback: ${computedCallback} times |
useMemo: ${computedMemo} |
App lifetime: ${second}sec.
`;
}
const tenThousand = 10 * 1000;
let expensiveCalcExecutedTimes = { 'useCallback': 0, 'useMemo': 0 };
function expensiveCalc(hook) {
let i = 0;
while (i < tenThousand) i++;
return ++expensiveCalcExecutedTimes[hook];
}
ReactDOM.render(
React.createElement(App),
document.querySelector('#app')
);
<h1>useCallback vs useMemo:</h1>
<div id="app">Loading...</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
解決方法は?
TL;DR。
-
useMemo
は、関数の呼び出しとレンダリングの間で計算結果をメモすることです。 -
useCallback
は、レンダー間でコールバック自体をメモすることです(参照一致)。 -
useRef
は、レンダリング間でデータを保持することです(更新しても再レンダリングが発生しない)。 -
useState
はレンダリング間にデータを保持することです(更新すると再レンダリングが発生します)。
ロングバージョンです。
useMemo
は、重い計算を避けることに重点を置いています。
useCallback
のようなインラインイベントハンドラで発生するパフォーマンスの問題を修正します。
onClick={() => { doSomething(...); }
原因
PureComponent
子プロセスの再レンダリング (関数式が毎回異なるため)
これは言った。
useCallback
の方が近いです。
useRef
というより、計算結果をメモするための方法です。
を調べてみると ドキュメント 確かにそこは混乱しそうですね。
useCallback
は、入力のいずれかが変更された場合にのみ変更される、コールバックのメモ化されたバージョンを返します。これは便利です。 最適化された子コンポーネントにコールバックを渡すとき、不必要なレンダリングを防ぐために参照の等価性に依存します。 (例: shouldComponentUpdate)。
例
仮に
PureComponent
-をベースにした子
<Pure />
を使用すると、一度だけ再レンダリングが行われます。
props
が変更された場合。
このコードでは、親が再レンダリングされるたびに子も再レンダリングされます - インライン関数は毎回参照先が異なるからです。
function Parent({ ... }) {
const [a, setA] = useState(0);
...
return (
...
<Pure onChange={() => { doSomething(a); }} />
);
}
の助けを借りて処理すればいいのです。
useCallback
:
function Parent({ ... }) {
const [a, setA] = useState(0);
const onPureChange = useCallback(() => {doSomething(a);}, []);
...
return (
...
<Pure onChange={onPureChange} />
);
}
しかし、一度
a
が変更されていることがわかります。
onPureChange
ハンドラ関数を作成し、React がそれを記憶してくれているのですが、その関数はまだ古い
a
の値です! パフォーマンスの問題ではなく、バグが発生してしまったのです! これは
onPureChange
へのアクセスにクロージャを使用しています。
a
変数が捕捉されたときに
onPureChange
が宣言されました。この問題を解決するには、React にどこで
onPureChange
を作成し、正しいデータを指す新しいバージョンを再作成/記憶(メモ化)します。これを行うには
a
として
依存関係
を `useCallback .' の第2引数に指定します。
const [a, setA] = useState(0);
const onPureChange = useCallback(() => {doSomething(a);}, [a]);
さて、もし
a
が変更されると、Reactはコンポーネントを再レンダリングします。そして、再レンダリング中に
onPureChange
が異なるため、新しいバージョンのコールバックを再作成/メモライズする必要があります。ようやくすべてがうまくいきました。
NBだけでなく
PureComponent
/
React.memo
の依存関係として何かを使用する場合、参照性の等質性が重要になることがあります。
useEffect
.
関連
-
[解決済み】Warning.Itが表示されるのはなぜですか?Functions are not valid as a React child?
-
[解決済み] error 'document' is not defined : eslint / React
-
[解決済み] sh: react-scripts: npm start の実行後にコマンドが見つからない。
-
[解決済み] React QueryとReduxの主な違いは何ですか?
-
[解決済み] React NativeとReactの違いは何ですか?
-
[解決済み] npxとnpmの違い?
-
[解決済み] Reactのstateとpropsの違いとは?
-
[解決済み] React / React Nativeでコンストラクタを使用する場合とgetInitialStateを使用する場合の違いとは何ですか?
-
[解決済み] Reactでes6クラスを使うときの「super()」と「super(props)」の違いとは?
-
[解決済み】 `useRef` と `createRef` の違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】プリセットファイルはオブジェクトのエクスポートができない
-
[解決済み] Jestの `beforeEach` グローバルは何のためにあるのですか?
-
[解決済み] error 'document' is not defined : eslint / React
-
[解決済み] バベルエラーです。JSX値は、式または引用されたJSXテキストのいずれかである必要があります。
-
[解決済み] リアクトです。<tr>は<td>の子として表示できません。コメント > td > tr を参照してください。
-
[解決済み] react nativeで関数だらけのヘルパーファイルを作成する方法は?
-
[解決済み] カスタマイズ素材UI チェックした場合としない場合の切り替え
-
[解決済み] react.jsでng-ifに相当するものは何ですか?
-
[解決済み] nextjsで異なる.envファイルを使用するには?
-
[解決済み] Reactのrender()にFont Awesomeのアイコンを入れる方法