- Published on
React useCallbackの使い方と使い所
ReactのuseCallback
の役割とuseCallback
をいつ使えば良いのかを紹介したいと思います。
主にuseCallback
は``useCallback(fn, deps)`で定義され、despが変わらない限り、新しい関数のインスタンス(fn)を作りません。これだけだと、なんのために存在しているかわからないと思います。
そこで、useCallback
が主に解決している問題を紹介します。
Memo化したコンポーネントが親で作成した関数をpropsとして指定しているばかりに、Memo化したにも関わらず、親が再描画されたときに子コンポーネントも意図せずに再描画されてしまいパフォーマンスが低下する問題があります。これを解決するのがuseCallback
の主な役割です。
例えば、下記のようにReact.memoを使って、コンポーネントをメモ化しているとします。 (※ React memoの使い方と使い所はこちらを参照)
ただ、下記のような場合だとせっかくメモ化しても、count
の変数が変わるたびにApp
が再度実行され、その際のconst getFavoritFood
も実行され新しいインスタンスができ、新しいインスタンスのため、
FavoriteFood
のプロパティが新しくなるため、FavoriteFood
が再描画されてしまいます。
( count
が変わるたびに console.log('get favorite function is called')
を確認できるので見てみてください)
このようにjavascriptでは、functionはobjectでありfunctionを定義すると新しいインスタンスを作り出します。 そして、新しいインスタンスであるので、それを参照している子コンポーネントが影響を受けてしまいます。
このような親のコンポーネントが再描画された時に、functionの新しいインスタンスが作成されるのを防ぐ役割を担うのがuseCallback
です。useCallback
はuseCallback( () => {}, deps)
で定義され、despが変わらない限り、新しい関数のインスタンスを作りません。
そうすることで、今回のような例のケースでもuseCallbackでgetFavoriteFoodをラップすることで、メモ化が意図した通りに動くようになります。
// React.memoが正常に動作していない例 import React, { useState } from "react"; const FavoriteFood = React.memo(({food}) => { const [msg, setMessage] = useState('') console.log('FavoritFood comp is called') return ( <> <p>好きな食べ物は: {msg}</p> { msg ? ( <button onClick={ () => setMessage('') }>隠す</button> ) : ( <button onClick={ () => setMessage(food) }>好きな食べ物を表示する</button> ) } </> ) }) const App = () => { const [count, setCount] = useState(0) const getFavoritFood = () => { console.log('get favorite function is called') return "りんご" } console.log("App is called") return ( <> <p>{count}</p> <button onClick={() => setCount(count + 1)}>++</button> <FavoriteFood food={getFavoritFood} /> </> ); } export default App;
useCallback
を用いて、メモ化を意図通りに動くようにするためには、下記のようにコードを変更したらApp
コンポーネントが再描画されても、getFavoriteFood
は再度実行されないので、FavoriteFood
コンポーネントが実行されなくなり、描画の最適化ができるようになります。
( count
が変わるたびに console.log('get favorite function is called')
が表示されなく、FavoriteFoodコンポーネントが描画されるときのみに実行されるのがわかるかと思います。)
// useCallbackを用いることで、React.memoが正常(意図した通り)に動作している例 import react, { usecallback, usestate } from "react"; const favoritefood = react.memo(({food}) => { const [msg, setmessage] = usestate('') console.log('favoritfood comp is called') return ( <> <p>好きな食べ物は: {msg}</p> { msg ? ( <button onclick={ () => setmessage('') }>隠す</button> ) : ( <button onclick={ () => setmessage(food) }>好きな食べ物を表示する</button> ) } </> ) }) const app = () => { const [count, setcount] = usestate(0) const getfavoritfood = usecallback(() => { return "りんご" },[]) console.log("app is called") return ( <> <p>{count}</p> <button onclick={() => setcount(count + 1)}>++</button> <favoritefood food={getfavoritfood} /> </> ); } export default app;
このように、function自体の参照を保存しておき、
新しいインスタンスを無駄に作らないことで、その関数を使っているコンポーネントが
無駄に再度描画されるのを防ぐのがuseCallback
になります。
useCallbackとuseMemoは似ているので、違いが気になる場合は、useMemoの使い方と使い所を参考にしてみてください。
参考: Your Guide to React.useCallback()
参考: What's the difference between useCallback and useMemo in practice?