ReactHookのuseCallbackは不要なレンダリングを防いでパフォーマンスを良くするための仕組みでuseMemoと似ています。
- useMemo → 関数の結果をキャッシュ
- useCallback → 関数自体をキャッシュ
下記のようなプログラムを作成してuseCallbackの働きを確認してみます。
上部はプラスボタンを押すと1ずつカウントがUPし、下部はマイナスボタンを押すと1ずつカウントがDownします。
App.jsの他に2つモジュールを作ります。カウンターの値を表示させる部分を担当する「CounterView」、カウンターのボタンを担当する「CounterButton」になります。
- src/App.js 主なプログラムを記載。CounterView.js , CounterButton読込
- src/components/CounterView カウンターの値を表示
- src/components/counterButton カウンターのボタン部分
useCallbackを使わない場合
App.js
import './App.css';
import { useState } from 'react'
import CounterView from './components/CounterView';
import CounterButton from './components/CounterButton';
function App() {
const [countA, setCountA] = useState(0)
const [countB, setCountB] = useState(0)
const PlusCount = () => {
setCountA(prevCountA => prevCountA + 1)
}
const MinusCnount = () => {
setCountB(prevCountB => prevCountB - 1)
}
return (
<div className="App">
<CounterView name = 'countA' count ={countA}/>
<CounterButton handleClick ={PlusCount}>プラス1</CounterButton>
<hr></hr>
<CounterView name = 'countB' count ={countB}/>
<CounterButton handleClick ={MinusCnount}>マイナス1</CounterButton>
</div>
);
}
export default App;
import React from 'react'
const CounterView = ({ name, count }) => {
console.log(`表示名: ${name}`)
return (
<div>
<h3> {count}</h3>
</div>
)
}
export default CounterView
import React from 'react'
const CounterButton = ({ handleClick, children }) => {
console.log('ボタン処理 : ', children)
return (
<div>
<button onClick={handleClick}>{children}</button>
</div>
)
}
export default CounterButton
動作チェク
ブラウザを開いたとき(初期のマウント)のログは下記のようになります。
「CounterView」「CounterButton」のモジュールがプラスボタン(A)、マイナスボタン(B)の2つ分動いています。
プラス・マイナスボタンどちらを押しても、上記のログが1セットずつ増えていきます。モジュールが少ないうちは良いですが、モジュールが多くなったり、負荷の高いモジュールが含まれていたりすると厄介そうなのはわかりますね。
このプログラムを関数の結果をキャッシュする部分(useMemo)と関数そのものをキャッシュする部分(useCallback)に分けていきたいと思います。
useMemoを導入
表示する値や関数の結果値をuseMemo化するのは簡単で、モジュール最後のexport defaultの後ろに来るモジュール名を React.memo() で囲むだけです。
export default React.memo (CounterView)
export default React.memo (CounterButton)
動作結果
プラスボタン(A)を押した場合のlogです。値のキャッシュの効果が現れ、countBは動かなくなりました。ボタン処理のプログラムは両方とも動いてしまっています。
今度はマイナスボタン(B)を押してみます。表示はBの処理のみが動いています。ボタンの処理は両方動いています。
useCallbackの導入
今回のプログラムの場合、useCallbackはApp.jsを変更するだけになります。
まず上部で useCallback をインポートします。
import './App.css';
import { useState } from 'react'
import { useCallback } from 'react'
import CounterView from './components/CounterView';
import CounterButton from './components/CounterButton';
function App() {
const [countA, setCountA] = useState(0)
const [countB, setCountB] = useState(0)
const PlusCount = useCallback(() => {
setCountA(prevCountA => prevCountA + 1)
},[countA])
const MinusCnount = useCallback(() => {
setCountB(prevCountB => prevCountB - 1)
},[countB])
return (
<div className="App">
<CounterView name = 'countA' count ={countA}/>
<CounterButton handleClick ={PlusCount}>プラス1</CounterButton>
<hr></hr>
<CounterView name = 'countB' count ={countB}/>
<CounterButton handleClick ={MinusCnount}>マイナス1</CounterButton>
</div>
);
}
export default App;
プラス1、マイナス1を計算する関数部分を callback()で囲みます。関数の中に第二引数を作りその中に動作の発火点となる変数(依存配列)を設定します。「const PlusCount」「const MinusCnount」の2ヶ所を変更します。
書式→ useCallback( ( ) => { 関数名 ( 具体的な処理 ) },[countB] )
結果
●ローディング時(初期マウント)は全部が動きます。
●プラスボタンを押したときはプラスに関連する処理のみが動きます。
●同様に、マイナスボタンを押したときだけはマイナスに関連する処理のみが動きます。
useCallBackに空配列を指定
上記では配列の中に変数を設定しましたが、今回の場合は変数を空に設定できます。