はじめに
今回はReact Hooksについてのまとめ記事の第二回目になります。
useMemo
単純に値を保存するためのフックです。
useStateと違い、更新関数はありません。
例えば「重い計算だけど、何回やっても結果は同じ」値などを保存する使い道が便利です。要はキャッシュですね。
また、コンポーネントの再レンダー時に値を再利用できるため、値の不要な再計算をスキップできます。
値の不要な再計算をスキップすることで、パフォーマンスの向上を期待できます。
構文
useMemo(() => 値を計算するロジック, 依存配列);
依存配列は、値を計算するロジックが依存している値(値の計算に必要な値)が格納された配列です。
例えば、countという変数の値を2倍にした値をメモ化したい場合は次のようになります。
const result = useMemo(() => count * 2, [count]);
依存している値が更新されれば、値が再計算されます。
そのため、上記のコードの場合、countが更新されない限り、値が再計算されません。
使用例
import React, { useState, useMemo } from "react";
export default function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
// 引数の数値を2倍にして返す。
// 不要なループを実行しているため計算にかなりの時間がかかる。
const double = (count) => {
let i = 0;
while (i < 1000000000) i++;
return count * 2;
};
// count2 を2倍にした値をメモ化する。
// 第2引数に count2 を渡しているため、count2 が更新された時だけ値が再計算される。
// count1 が更新され、コンポーネントが再レンダーされた時はメモ化した値を利用するため再計算されない。
const doubledCount = useMemo(() => double(count2), [count2]);
return (
<>
<h2>Increment(fast)</h2>
<p>Counter: {count1}</p>
<button onClick={() => setCount1(count1 + 1)}>Increment(fast)</button>
<h2>Increment(slow)</h2>
<p>
Counter: {count2}, {doubledCount}
</p>
<button onClick={() => setCount2(count2 + 1)}>Increment(slow)</button>
</>
);
}
useCallback
useCallbackはuseMemoの亜種で、メモ化されたコールバック関数を返すフックです。
使い所としては名前の通りコールバック関数のキャッシュです。関数コンポーネントが実行されるたびにコールバック関数を新しく作っていると、「コンポーネントに新しいコールバック関数が設定された」とReactは認識し、再レンダリングが実行されます。そこでuseCallbackを使ってキャッシュしておけば、前と同じ関数ということを認識してくれて、再レンダリングを抑制できます。
また、useCallbackはReact.memoと併用する前提です。
構文
useCallback(コールバック関数,依存配列);
依存配列は、コールバック関数が依存している値が格納された配列です。
依存している値が更新されれば、関数が再生成されます。
そのため、上記のcallback関数の場合、countが更新されない限り、関数は再生成されません。
// console.logでcountという変数を出力する関数をメモ化したい場合は下記のようになります。
const callback = useCallback(() => console.log(count), [count]);
// 依存している値がなければ、次のように依存配列は空でOK。
const callback = useCallback(() => console.log('doSomething'), []);
使用例
import React, { useState, useCallback } from 'react';
const Child = React.memo(({ handleClick }) => {
console.log('render Child');
return <button onClick={handleClick}>Child</button>;
});
export default function App() {
console.log('render App');
const [count, setCount] = useState(0);
// 関数をメモ化すれば、新しいhandleClickと前回のhandleClickは等価になる。
// そのため、Child コンポーネントは再レンダーされない。
const handleClick = useCallback(() => {
console.log('click');
}, []);
return (
<>
<p>Counter: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment count</button>
<Child handleClick={handleClick} />
</>
);
}
useLayoutEffect
useLayoutEffectはuseEffectの亜種です。
機能としては同じで、遅延実行、クリーンアップ、依存関係配列を持ちます。
違いは実行タイミングです。useEffectが「レンダリングが完全に完了した後」に実行されるのに対して、useLayoutEffectは「DOMに要素が追加され、ブラウザが表示する直前」に実行されます。
また、useLayoutEffectはレンダリングをブロックします。
そのため、重い処理を書くとレンダリングが遅れてしまうので注意が必要です。
このフックは使用する場面がほとんどないため覚えなくて良さそうです。(useEffectで十分です)