今、社内のプロジェクトでシステムを開発していてフロントエンドをやらせてもらってるんですが
その中でReact Hooksを勉強しています。
ということでその中で使用したuseCallbackと、HooksではありませんがReact.memoに関して
初学者の方でも分かっていただけるように書いていければと思います。
Hookに関して
Hooksなん?Hookなん?
とか少し思ってたんですが、これは複数形ですね。
いくつかHookがあるんで、Hooksと呼ぶんだと思います。
一旦公式を。フックの導入
なんでも、Reactが開発されてから色んな問題に直面することがあったらしく、それらを解決するために導入されたようです。
Hookがなんたるか、知ることも大切ですが今回はその中でもuseCallbackに関して書いていくのでここでは詳細は省きます。公式をご覧ください。
#useCallbackを使用すると何が良いの?
Reactは本来ストアされている情報をレンダリングするために作られたフレームワークです。
コンポーネントなどではストアの情報に変更があった際にその変更に伴って再レンダリングが発生して
描画しなおしています。
ここで、問題になるのは関数などはどうなるのか?
以下のような状態を考えます。
ちなみに、Material UIのサンプルコードです。
export default function SimpleSnackbar() {
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(true);
};
const handleClose = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setOpen(false);
};
return (
<div>
<Button onClick={handleClick}>Open simple snackbar</Button>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={open}
autoHideDuration={6000}
onClose={handleClose}
message="Note archived"
action={
<React.Fragment>
<Button color="secondary" size="small" onClick={handleClose}>
UNDO
</Button>
<IconButton size="small" aria-label="close" color="inherit" onClick={handleClose}>
<CloseIcon fontSize="small" />
</IconButton>
</React.Fragment>
}
/>
</div>
);
}
先ほども言ったように、ストアされている情報に変更があった際にはこのコンポーネントには再レンダリングがかかります。
すると、コンポーネント内部で定義されているhandleCloseなども再定義されて、関数が作成されてしまうことになります。
ですが、あくまでここで使われているhandleClose自体の引数に変化があったりしたかと言われるとそうではないため、ここで再び作成されるhandleCloseは最初にレンダリングされた際に定義されたものと全く同じものになります。
このように、レンダリングされるたびに同じ関数が何度も何度も作成されてしまうのです。
これでは無駄すぎるから、引数に変化があったりしたときだけ関数を定義し直して欲しい!!!
ここで使用されるのがuseCallbackです。
どういった仕組みでレンダリングの際に定義されることを防いでいるか詳細まではわかりませんが、
感覚的には一旦退避場所に逃しておく感じ。
こうすることで、コンポーネントは無駄な関数を再定義して作成し続ける仕事をしなくてよくなるので
パフォーマンスは向上されます。
#じゃあ、React.memoは?
では、useCallbackがどんなことをしてくれるかなんとなく分かったところで
同じことがコンポーネントにも言えるのでは?と考えられます。
ここでも簡単な例をあげます。
const Index = () => {
return (
<Header />
<MainSection />
<Footer />
);
};
export default Index;
こんな感じで、あるウェブサイトのトップページが記載されているとします。
このような場合ヘッダーやフッターは基本的に固定の内容が記載されるはずなので、
ストアの情報が変更されるときに、再レンダリングがかかるべきはMainSectionのコンポーネントのみということになります。
ですが、普通に書いてしまうと全部が再レンダリングされてしまう。
ここでReact.memoを使います。
こちらも公式のドキュメントのリンクを貼っておきます。React.memo
const Header = React.memo(function Header(props) {
/* render using props */
});
const Footer = React.memo(function Footer(props) {
/* render using props */
});
このようにHeaderとFooterを定義してあげると、同じpropsを与えられたときに同じ結果をレンダーするなら、結果を記憶してくれます。コンポーネントのレンダーはスキップされて、最後のレンダー結果を再利用するようになります。
そのため、無駄なくレンダリングできるようになるのでパフォーマンスが向上するというわけです。
#適切に使用して良いコンポーネントを作成しましょう
このように、パフォーマンスを向上させるためにuseCallbackやReact.memoを利用してコンポーネントの作成を行いました。
今後はもっと勉強してより質の高いコードをかけるようにしていければと思います。
また、ここで書いているのは僕の備忘録なので表現だったり理解が誤っている可能性がないとは言えません。
あくまで、そういう物があるんだなくらいに初学者の方々には読んでいただけるとありがたいです。