In this post we'll understand what is React key prop used for, and how should we use it
React has it's virtual DOM, it applies changes on this virtual DOM, then calculates the difference between this virtual DOM and the real DOM that appears on browser
As we can see React has the virtual DOM in a tree, When a state changes it determines the affected nodes, then apply changes to them, then it applies differences to the real DOM
In case of rendering lists React needs to identify exactly each element, Why? Imagine having a simple component like this
const TodoList = () => {
const [todoList, setTodoList] = useState([
{id: 1, text: 'Playing football'},
{id: 2, text: 'Studying'},
{id: 3, text: 'Sleeping'}
]);
return (
<ul>
{todoList.map((item) => (
<li>{item.text}</li>
))}
</ul>
);
};
export default TodoList;
In virtual DOM this component will be presented like this
As we can see, the li elements depends on the todoList state, And every time we set this state (Adding, modifying or removing items from the todoList) we'll be setting a new array to it with a new reference, So React will take the new array and create new li elements using the new array ... Here exactly React needs something that identify the old elements that were already rendered using the previous state not to recreate them, That's why they created the Key Prop
To have a valid Key prop, it should
- Have a constant value that doesn't change with each render, so we shouldn't do something like this
return (
<ul>
{todoList.map((item) => (
<li key={Math.random()}>{item.text}</li>
))}
</ul>
);
Math.random() will be called with every render and generate a new random value, So every render React will identify all elements as new elements and create them from scratch which leads to a weak performance
Instead, We should have a key that doesn't change with new renders, Like id coming from DB or even an id generated with Math.random() but created in the initialization only
const TodoList = () => {
const [todoList, setTodoList] = useState([
{id: Math.random(), text: 'Playing football'},
{id: Math.random(), text: 'Studying'},
{id: Math.random(), text: 'Sleeping'}
]);
return (
<ul>
{todoList.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
};
- It should be unique per siblings, So each sibling should have a different key than it's siblings
*Why shouldn't we use index as a key?
*
return (
<ul>
{todoList.map((item, index) => (
<li key={index}>{item.text}</li>
))}
</ul>
);
React uses index as a default key if We don't assign a key, But the risk here is that if We add an element to the beginning of the array it'll have key=0 which was the key of the current second element of the array, Same thing will happen if we add or remove elements from the array.
This will be very clear if we use inputs as an example
{todoList.map((item, index) => (
<li key={index}>
<p>{item.text}</p>
<input />
</li>
))}
The first item here has a key=0, If we write anything in it's input, then add a new item to the beginning of our todo list, The new added item will have key=0, and the old item which we've written in it's input will have key=1, Now on the screen we'll see what We've written in the second item appearing in the first item cause React identifies the element only by it's key ... That's why index is not preferred to be used as a key
There's another one good hack about keys, For React a new key means a new element with new states ... And sometimes we need to reset component states, What we usually do in this case we create a useEffect and listen to a variable then reset states when this variable changes, Like this example
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
const [likes, setLikes] = useState([]);
useEffect(() => {
setPosts([]);
setComments([]);
setLikes([]);
}, [useId]);
Instead of doing this, We can simply pass userId as a key to this component and when userId changes, it'll have a new key and a *new states
*
<UserProfile key={userId} />
Conclusion
- Key is important for React not to recreate list items
- Key must be unique for each sibling
- Key must have a constant value that doesn't change while rendering
- Index isn't preferred to be used as a key
- Key can be used to reset component states
Thank u!
Top comments (0)