関数式による再レンダリング問題
再レンダリングのタイミングには以下の2つがある
- stateの更新
- propsの更新
(以下ではAコンポーネントの中でBコンポーネントが呼び出されている)
ここで、Aコンポーネント内のstateが更新されたとする。
すると再レンダリングが起きてAコンポーネント内を上から再度読み込んでいく。
このとき、Aコンポーネント内に関数式があれば再代入される。
再代入された関数式は以前の関数式と処理の内容は変わらないものの、Reactは全く新しい関数として判断する。
このとき、関数式をBコンポーネントにpropsとして渡しているとする。以前のものと異なる関数式がBコンポーネントのpropsとして渡されるので、Bコンポーネントで再レンダリングが起きてしまう。
上記のようにAコンポーネント内のstateが更新されただけなのに、Bコンポーネントまでもが再レンダリングされていることを「関数式による再レンダリング問題」と定義する
Bコンポーネントのmemo化による解決
memo化では解決できない
useCallbackによる解決
関数式をuseCallbackの引数に渡すことで再レンダリングを操作することができる。
第1引数には関数式、第2引数には配列を設定する
const test = useCallback(()=>{}, [])
第2引数の配列を依存配列といい、依存配列の値が更新された場合のみ関数が再定義されるようになる。
下記の場合は、hogeが更新されたときのみ、関数式の再代入が起きてBコンポーネントが再レンダリングされる。
const [hoge,setHoge] = useState();
const test = useCallback(()=>{}, [hoge])
useCallbackの注意点
useCallbackは依存配列が更新されない限り、初回レンダリングの状態が維持される。
以下のように依存配列にflgを設定していないとflgがfalseのままになってしまい
「flgはfalse」というアラートしか表示されなくなる
const [flg,setFlg] = useState(false);
const test = useCallback(()=>{flg === true ? alert("flgはtrue": alert("flgはfalse"))}, [])
以下のようにすればOK。基本的に関数式の中で利用する変数や関数を依存配列に入れておけば問題はなさそう。
const [flg,setFlg] = useState(false);
const test = useCallback(()=>{flg === true ? alert("flgはtrue": alert("flgはfalse"))}, [flg])