親が key を変えることでコンポーネントが作り直されるけど 使う側で追加でやることを増やしたくない
忘れて思い通り動かないケースとか出てきそう
なので内部で制御するように useEffect を使って props の変化を検出して setState で更新ってやってることが多い
新しい作りかけドキュメントの方では useEffect をこの目的で使うことを推奨していなくて key を使うほうがいいと言ってる
ドキュメントにまで書かれるならその方向にしようかなと思うけど やっぱり面倒
間にコンポーネントを挟んで 指定の props の変化時に key の制御は自動でやるようにする
const withReset = (Component, names) => {
const diff = (a, b) => {
return names.some(name => a[name] !== b[name])
}
return (props) => {
const ref = useRef({ key: 0, props: {} })
if (diff(ref.current.props, props)) {
ref.current = { key: ref.current.key + 1, props }
}
return <Component key={ref.current.key} {...props} />
}
}
・使用例
Component のボタンを押すとカウントアップ
foo の入力が変わったときのみ key が新しくなってカウントがリセットされる
bar, baz を変更してもカウントは維持される
const Component = withReset(({ foo, bar, baz }) => {
const [num, up] = useReducer(s => s + 1, 1)
return (
<div>
<div><button onClick={up}>{num}</button></div>
<div>foo: {foo}</div>
<div>bar: {bar}</div>
<div>baz: {baz}</div>
</div>
)
}, ["foo"])
const App = () => {
const [foo, setFoo] = useReducer((_, eve) => eve.target.value, "foo")
const [bar, setBar] = useReducer((_, eve) => eve.target.value, "bar")
const [baz, setBaz] = useReducer((_, eve) => eve.target.value, "baz")
return (
<div>
<div>foo: <input value={foo} onChange={setFoo} /></div>
<div>bar: <input value={bar} onChange={setBar} /></div>
<div>baz: <input value={baz} onChange={setBaz} /></div>
<Component foo={foo} bar={bar} baz={baz} />
</div>
)
}
作ってみて 以前似たようなもの作った気がすると思って探すとあった
以前は useMemo を使ってたらしい
新規の key 作成と 変化ありの検出をまとめてできてるし良さそう
今のよりスッキリしてる
以前の方が良かったって成長してないどころか劣ってる……
でも useMemo って React がメモリの都合で勝手にメモを忘れて再計算するかもしれないって言ってるから安全ではないかも
リセット目的じゃない再レンダリングで state がクリアされてしまうと困るし
そう考えるとこれ以外でも変更検出のために useMemo 使ってるところ全部ダメそう
変更無いのに変更ありってみなされるケースも無いとはいえない
変更の有無は標準のフックで用意してほしい