Reactにおける再レンダリング
Reactで各compnentが再レンダリングされてしまう条件は以下の3つです。
- stateの更新
- propsの更新
- 親component の更新
1,2はいいでしょう。定義したstateやpropsが変化すれば当然componentは変化してほしいわけです。
(stateやpropsがバケツリレーされていないのが前提。もしその場合はグローバルな値にするとか別の方法探してください。)
問題は3です。
親componentを更新したいだけなのに、その中に含まれる子component(以下孫、ひ孫....)まで更新されちゃたまったもんじゃないですよね。
折角Reactで書いてるんだから、そんないかにも重くなりそうなことしたくない。。。
#そんな時には!
そんな問題を解決するためにはメモ化!
##memo
export const ExComponent = memo((props) => { return (hogehoge) })
memo()で括ってやれば、親componentの更新影響を受けなくなります。
いや待ってくれ!memo化しても更新されたぞ??嘘つきか?
例えばこんなcomponent。
export default function App() {
const [exText, setExText] = useState("");
const changeExText = (e) => {
setExText(e.target.value);
};
return (
<>
<Child changeText={changeText} />
</>
);
}
const Child = memo((props) => {
const { changeExText } = props;
return (
<>
<input type="text" onChange={changeExText} />
</>
);
});
親Componentで設定した関数を子Componentに受け渡して処理。
でも必要なステートは親の中だけだし、更新されるのは親だけと思っていたら、この場合、親子どちらも更新される。
memo化はしたのだが。。。
関数のメモ化はuseCallbackを使え
上記の原因はズバリpropsで受け渡した関数。
親コンポーネントのpropsで子コンポーネントに関数を渡すと、関数の内容が同じでも子コンポーネントでは「毎回新しい関数が渡されている」と判断されてしまうらしい。まあ確かにそう見える。。。
そんな時にuseCallbackの出番。
const changeExText = useCallback(
(e) => {
setExText(e.target.value);
},
[setExText]
);
useCallbackを使えば上記のような再レンダリングは起こらない。
使い方はuseEffectと同様なので、簡単だ。
まとめ
できるだけ重くないreactを書きたい時にはmemo/useCallbackを駆使して再レンダリングをできるだけ抑えるべし。