【React】 リストをレンダリングする際のkeyの必要性
Reactでfor文等を利用し、リストで要素を出力すると、key
の設定をするべきという警告が表示されます。ではkey
を使用することで何が良いのでしょうか。
keyの役割
リストの要素におけるkey
の役割は、Reactがそれぞれの要素を追尾できるようになるということです。これは、特に要素の追加や削除時に発生します。つまり、key
が必要になるのは、Reactで要素を配列で描画するときです。配列の要素が変化したときに、Reactがうまく追尾できるとパフォーマンスの向上ができます。
例えば、TODOリストのようなものを考えてみましょう。まずはkey
を使用しないパターンを実装してみます。
import TodoItem from './components/TodoItem'; import { useCallback } from 'react'; export default function App() { const [todos, setTodos] = useState([]); const addTodo = (e) => { e.preventDefault(); setTodos([...todos, { id: new Date().getTime(), content: e.target.content.value, }]); e.target.content.value = ""; }; const deleteTodo = useCallback((index) => { setTodos( todos.filter((_, i) => i !== index) ); }, [todos]); const renderTodos = () => { return todos.map((e, i) => <TodoItem content={e.content} deleteTodo={() => {deleteTodo(i)}} /> ); } return ( <div className="App"> <ul> {renderTodos()} </ul> <form onSubmit={addTodo} method="post"> <input type="text" name="content" placeholder="Todoの内容" /> <input type="submit" value="追加" /> </form> </div> ); }
/** * @typedef {{ * content: string * deleteTodo: () => void * }} Props */ /** * @param {Props} props */ export default function TodoItem(props) { return ( <li> <span style={{"marginRight": 12}}>{props.content}</span> <button onClick={props.deleteTodo}>削除</button> </li> ); }
TodoItem
は表示とボタンクリック時に呼ぶべき関数を受け取って表示するだけです。
App.jsx
が少し長いですが細かく読む必要はありません。追加、削除、表示の3つの関数があることと、renderTodos
の中身を把握すれば十分です。
実際に動作させると、特に問題なく動作します。しかし、consoleの表示を見ると、削除した際に、それより後ろの項目が全て再レンダリングされるということがわかります。
これは、例えば
- aaa
- bbb
- ccc
を表示していたとすると、ここからaaa
の項目を削除した際に、
- aaa → bbb
- bbb → ccc
- ccc → 消滅
という処理がされるためです。たしかに全ての値が変化しています。
keyを使って無駄な更新を防止
そこで、key
を使うことで項目を追尾し、無駄なDOMの更新を防ぎます。
const renderTodos = () => { // key={e.id} を追加 return todos.map((e, i) => <TodoItem content={e.content} deleteTodo={() => {deleteTodo(e.id)}} key={e.id} /> ); }
こうすることで、
- aaa → 消滅
- bbb → 変化なし
- ccc → 変化なし
という風に処理させることができ、DOMの再レンダリングを防ぐことができます。
keyに使う値
keyに使うべき値は、
- その一連のリストの中でユニークであること。例えばUIDなど
- 不変であること
を満たしていることが推奨されます。ユニークでない (他の項目とかぶる) 場合、うまく項目を追尾できない場合があります。
// GOOD 固有の変化しないIDを利用している return todos.map(e => <TodoItem key={e.id} /> ); // BAD ユニークではあるが値は毎回変化するので意味無し return todos.map(e => <TodoItem key={Math.random()} /> ); // BAD ユニークですら無い return todos.map(e => <TodoItem key={256} /> );
不変であることは、パフォーマンスを上げるにはほぼ必須です。しかし、UUIDなどのユニークな値が用意されていれば良いですが、ない場合は配列のインデックスを使わざるを得ません。配列のインデックスだと意味のある追尾ができないですが、最終手段として一応設定しておきましょう。
まとめ
key
は、Reactが要素を追尾できるようにするために使用するもので、リストのレンダリング時に用います。key
には、ユニークかつ変化しない値を用いるべきです。
無駄なレンダリングを防ぐために、必ず使用しておきましょう。
