ReactのListのkeyを正常に指定しないととんでもないことになる
今回もGyazoのテストも兼ねて、ReactのListのkeyについての投稿です。
あんまやる人いないと思うのですが、keyにを正常に指定、つまり一意なkeyを指定しないと全体的にレンダリングされてしまうので注意が必要です。
上記見ていただくとわかるのですが、Reactの差分検出処理は双方のリストの一致/不一致を走査し、不一致があればその差分を更新するというものです。
で、例でもあるのですが最後に追加された場合は、問題なく「差分は1つ」の更新になります。
ただ、先頭に追加された場合は最初から不一致となるので、「差分は3つ」となります。つまり全て差分がある、と認識されてしまうんですね。
そこで登場するのが、keyという概念。
上記では、単純なDOMの比較をしていましたが、keyを比較することにより、より正確かつ素早く差分を検出することができるようになります。
では、例を見てみましょう。
一意なkeyを指定した場合と、ランダムなkeyを指定した場合です。
ランダムなkeyを指定した場合(key = Math.random)
const CountUpList = () => { const [list, setList] = useState([{id: 1, name: 'test1'}]); const countRef = useRef(1); const handleCountUp = () => { countRef.current = countRef.current + 1; const addedObj = { id: countRef.current, name: `test${countRef.current}` } setList([...list, addedObj]) } return ( <> <div> <div>カウント: {countRef.current}</div> <button onClick={handleCountUp}>カウントアップ</button> </div> <ul> {list.map(el => <li key={Math.random()}>{el.name}</li> )} </ul> </> ) }
正常なkeyを指定した場合(key = el.id)
const CountUpList = () => { const [list, setList] = useState([{id: 1, name: 'test1'}]); const countRef = useRef(1); const handleCountUp = () => { countRef.current = countRef.current + 1; const addedObj = { id: countRef.current, name: `test${countRef.current}` } setList([...list, addedObj]) } return ( <> <div> <div>カウント: {countRef.current}</div> <button onClick={handleCountUp}>カウントアップ</button> </div> <ul> {list.map(el => <li key={el.id}>{el.name}</li> )} </ul> </> ) }
ご覧の通り、ただkeyを指定すればよいというわけではなく、このList内で一意なkeyを設定することが必要になります。
Math.randomだと重複ありますからね。
APIの返り値を表現する際にもListは結構使われると思いますが、APIの設計もちゃんとこの辺りを考えられると良いですね。
惰性で振っているIDかもしれませんが、SPAで構築する際にはわりと重要なポイントになってくると思います。
ということで、やっぱGyazoいいですな...