業務でRailsアプリをRails + React/Reduxへのリプレイスを行いましたが、不要なレンダリングなどが目立ちパフォーマンスがよろしくないので、そろそろuseCallback
とuseMemo
の出番かなと思い調べてみました。
僕自身の理解度は
useMemo
= 値をメモ化する
useCallback
= コールバック関数を記憶する。イベントハンドラなどに使用する
くらいの知識量です。
useMemo
公式ドキュメントでは次のように説明しています。
メモ化された値を返します。
そのまんまじゃねーか...
Reactのfunctionコンポーネントは主に以下のタイミングで再レンダリングが行われます。
- 親コンポーネントがレンダリングされたとき
- 親コンポーネントから渡されているpropsに変更があったとき
- コンポーネント内にあるuseStateで定義している変数に変更があったとき
使っているときはなんとなくこのくらい再レンダリングされる要素があるんだなと感じますが、文字に起こすと結構な頻度で再レンダリング走ることがわかりますね。
このときに処理をレンダリングされるたびに行っていたらパフォーマンス悪いよねということでメモ化しようというのがuseMemo
です。
使い方は以下のようになります。
import React, { useMemo } from 'react'
const Foo = useMemo(() => {
// 処理
return hogehoge
}, [依存変数])
メモ化したい処理を書いてreturnするだけですね。
注意が必要なのはuseMemo
の第二引数です。
useEffectなども同じなのでわかりやすいですが、第二引数は依存関係を表しています。
動作は2パターンあります。
- 空配列を渡した場合は初回の一度だけ実行される
- 変数を渡した場合は、初回と渡した変数に変更があった場合に処理が実行される
例えば、以下のコードは変数yに依存しているので変数yに変更があった場合は処理にも実行されます。(メモ化する処理ではないですが目をつむってください)
const square = useMemo(() => {
return y ** y
}, [y]
第二引数にはなにかしら変数が渡されていることが多いイメージですね。
useCallback
公式ドキュメントでは次にように説明されています。
メモ化されたコールバックを返します。
...知ってた
コールバック関数も再レンダリングされるたびに関数インスタンスを生成されてしまうのでメモ化することでパフォーマンスを改善できます。
useCallback
の使い方はuseMemo
とほぼほぼ同じです。(これもメモ化する必要はないです...)
import React, { useState, useCallback } from 'react'
const [count, setCount] = useState(0)
const onClick = useCallback(() => {
setCount(count + 1)
}
return <Button onClick={onClick}>
主にイベントハンドラを使うときに使うと簡単に言ってしまいたいのですが、useCallback
の使い所は少々難しいです。
詳しく解説している記事をqiitaにて発見しましたので、ご興味ある方はぜひご覧ください。
React HooksのuseCallbackを正しく理解する
あとがき
useMemo
もuseCallback
も使い方自体はすごく簡単ですが、適切に使用しないとパフォーマンス上がらないというのが最大の難所だと感じています。
僕の知識が深まってきたら改めて記事にしたいです。
あと、ここらへんの簡単なガイドラインなどあったらほしいですね。
余談ですが、qiitaに記事を投稿するの初めてなのですごい緊張しました。
一回投稿したことで心理的障壁が下がることを期待。