最近 Hooks を触って思ったことを綴ります。よく見る useState の increment 例です。
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(count + 1)
}
return (
<div className={props.className}>
<p>count: {count}</p>
<button onClick={handleClick}>+1</button>
</div>
)
これはアンチパターンで、handleClick
は render毎に再定義されます。この再定義を skip するため、useCallback
による関数memoizeを行います。
アンチパターン集
// memoize input array の指定がないため、上記と差がない
const handleClick = useCallback(() => {
setCount(count + 1)
})
// memoize されているが、状態変化とともに再定義される
const handleClick = useCallback(() => {
setCount(count + 1)
},[count])
// 2回目以降のレンダリング時再定義が skip されるが
// 初期状態を参照しているため「1」にしかならない。
const handleClick = useCallback(() => {
setCount(count + 1)
},[])
現状良さそうな方法
状態更新関数の引数に与えるものは、プリミティブに限りません。関数を与えることで、prev state を参照出来ます。これにより、初期レンダリング時のみに handleClick関数定義を抑止し、正しく動かすことが出来ます。
// 2回目以降のレンダリング時の再定義が skip され、正しく動く
const handleClick = useCallback(() => {
setCount(prev => prev + 1)
},[])
useState で生成された関数は、同時に生成された状態を参照しない方が良さそう、という話でした。