DEV Community

Remon Fawzi
Remon Fawzi

Posted on

What you should know about React Key Prop

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

Image description

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;
Enter fullscreen mode Exit fullscreen mode

In virtual DOM this component will be presented like this
Image description

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

  1. 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>
);
Enter fullscreen mode Exit fullscreen mode

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>
  );
};
Enter fullscreen mode Exit fullscreen mode
  1. 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>
  );
Enter fullscreen mode Exit fullscreen mode

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>
   ))}
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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} />
Enter fullscreen mode Exit fullscreen mode

Conclusion

  1. Key is important for React not to recreate list items
  2. Key must be unique for each sibling
  3. Key must have a constant value that doesn't change while rendering
  4. Index isn't preferred to be used as a key
  5. Key can be used to reset component states

Thank u!

Oldest comments (0)