React generates this warning when you render a list of elements without a key for each element. When you render a list of elements or components, React expects a unique key for each element, mainly for performance improvements. Refer to the Reconciliation section of the official React docs.
Why each child in a list needs a unique key?
React needs a unique key to identify each list item uniquely. That is important when you render a long list and later perform changes to that list. Keys play an important role in below operations. The key should be unique and remain the same during each re-render. Random numbers should never be assigned as keys.
- Inserting a new item into a list
- Deleting an item from a list
- Editing an existing item in a list
- Sorting a list
Why are keys important when inserting a new item into a list?
React identifies each list item by its key.
- Append an item into an existing array in a state variable
- React detects the state change and re-renders the array
When React re-renders the array, it uses the key to identify each item. Therefore, React leaves existing items as they were, and only inserts the new item. So, no re-rendering happens to existing items.
Without keys in the list items, React cannot identify which element got inserted into the array. React only sees the mismatch between the state array and rendered array. To match them, React adds an extra item into the dom array and re-renders the entire array to reflect the new addition.
Why are keys important when deleting an item from a list?
Without keys in the list items, React doesn't know which item got deleted. Therefore, React re-renders the entire list to reflect the state array in which the item got deleted.
Why are keys important when editing an existing item in a list?
Just like when inserting and deleting, if React can identify the exact item that got edited, React can re-render only that edited item, leaving the rest of the dom array untouched. Keys help uniquely identify items in the array.
Why are keys important when sorting a list?
Sorting is re-arranging the same set of items in a different order. Without a key, React re-renders the entire list when sorting occurs. In order for sorting to work efficiently, you should have a unique constant key attached to each item in the list. Randomly generated numbers or array indexes will not work.
What if I don't use a key?
If you don't use a key, React will use the array indexes as the keys. That is ok, as long as you don't mutate or sort the list. Since the first list item key will always be 0 and the last item key will always be the length of the array, there will be no re-renders as long as the state array remains unchanged. But if you mutate or sort the state array, the key association will change, causing a re-render.
Randomly generated number as the key
Always avoid assigning random numbers as keys. No matter what, React will always re-render the list. That is because keys change every time the component re-renders. Example React code to demonstrate insert, delete and sort
Below is a sample React code to demonstrate inserting, deleting, and sorting a list with and without keys. Feel free to remove the key from
App.tsx
import { useState } from 'react';
import Tr from './components/Contact';
export const App = () => {
const [arr, setArr] = useState([
{id: 1, name: "A1", score: "90"},
{id: 2, name: "A2", score: "91"},
{id: 3, name: "A3", score: "92"}
])
const style = {border: "1px solid black", "borderSpacing": "5px"}
const addRowTop = () => {
const l: number = arr.length+1
const newArr = [{id: l, name: `A${l}`, score: 92+l+""}]
setArr(newArr.concat(arr))
}
const addRowBottom = () => {
const l: number = arr.length+1
const newArr = [{id: l, name: `A${l}`, score: 92+l+""}]
setArr(arr.concat(newArr))
}
const deleteRow = (id: number) => {
const newArr = arr.filter((item) => item.id != id)
setArr(newArr)
}
const sortArray = () => {
const sortedArr = [...arr]
sortedArr.sort((a,b) => a.id - b.id)
setArr(sortedArr)
}
return(
<>
<table style={style}>
<tbody>
{arr.map((val, i) =>
<Tr row={val} onDelete={deleteRow} key={Math.random()}/>
)}
</tbody>
</table>
<br/>
<button onClick={addRowTop}>Add Top</button>
<button onClick={addRowBottom}>Add Bottom</button>
<button onClick={sortArray}>Sort Array</button>
</>
)
}
Component rendered as list item
import { useEffect } from "react"
interface trow {row: {id: number, name: string, score: string}, onDelete: Function}
const style = {border: "1px solid black", "padding": "5px"}
const Tr = (tr: trow) => {
const {id, name, score} = tr.row
useEffect(() => {
console.log("Re-Rendered "+name)
}, []);
return <tr>
<td style={style}>{id}</td>
<td style={style}>{name}</td>
<td style={style}>{score}</td>
<td style={style}><button onClick={() => tr.onDelete(id)}>Delete</button></td>
</tr>
}
export default Tr