skip to Main Content

I’m doing some experiment to observe the performance disparity in React re-rendering between using array indices as key props and using unique values as key props. Surprisingly, using index as the key outperforms using values as keys. Here’s my testing approach: displaying a list of numbers from 0 to 9999 initially, then adding new numbers to the beginning of the array when the button is clicked.

Index as key:

https://codesandbox.io/s/silly-framework-khk243?file=/src/App.js

export default App = () => {
  const [arr, setArr] = useState(_.range(10000));
  return (
    <div className="App">
      <button onClick={() => setArr([arr[0] - 1, ...arr])}>unshift</button>
      <ul>
        {arr.map((k, i) => (
          <li key={i}>{k}</li>
        ))}
      </ul>
    </div>
  );
}

enter image description here

Unique value as key:

https://codesandbox.io/s/old-cherry-9tjffv?file=/src/App.jsx

export default App = () => {
  const [arr, setArr] = useState(_.range(10000));
  return (
    <div className="App">
      <button onClick={() => setArr([arr[0] - 1, ...arr])}>unshift</button>
      <ul>
        {arr.map((k, i) => (
          <li key={k}>{k}</li>
        ))}
      </ul>
    </div>
  );
}

enter image description here

To the best of my understanding, with unique value as the key, during the diffing phase, React identifies that the only difference lies in the first element, while the others remain unchanged. As a result, the only operation applied on the real DOM tree is the insertion of the new <li> element.

In the case of using the index as the key, updates are required for all 10,000 elements with shifted values, necessitating a total of 10,000 operations, plus one insertion of the new <li> element. I suppose this approach is significantly slower than the alternative, but it doesn’t appear to be the case.

Any idea what leads to the noticeable delay?

2

Answers


  1. Using values as keys (key={i}), React can easily recognize which element has been added or moved, as the indices will change when new elements are inserted at the beginning of the array. This makes the reconciliation process more efficient, as React can quickly identify the differences.

    On the other hand, using indices as keys (key={k}), React needs to compare each value with the previous virtual element to determine if it has been added, removed, or moved. This process could take more time, as it requires a more detailed comparison to establish the differences.

    Login or Signup to reply.
  2. As I’ve already said, your clues are absolutely correct, as the react docs say so https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key.

    You can check the examples and see the difference.

    If you use key, everything behaves correctly, as per docs.

    const {useState} = React;
    
    const testArr = Array.from({length: 10000}).map((_, index) => index)
    
    const App = () => {
      const [arr, setArr] = useState(testArr);
      return (
        <div className="App">
          <button onClick={() => setArr([arr[0] - 1, ...arr])}>unshift</button>
          <p>using keys</p>
          <ul>
            {arr.map((key, index) => (
              <li key={key}>{key}</li>
            ))}
          </ul>
        </div>
      );
    }
    
    
    ReactDOM.createRoot(
        document.getElementById("root")
    ).render(
        <App />
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
    <div id="root"></div>

    But if you use indexes, the render becomes slow.

    const {useState} = React;
    
    const testArr = Array.from({length: 10000}).map((_, index) => index)
    
    const App = () => {
      const [arr, setArr] = useState(testArr);
      return (
        <div className="App">
          <button onClick={() => setArr([arr[0] - 1, ...arr])}>unshift</button>
          <p>using indexes</p>
          <ul>
            {arr.map((key, index) => (
              <li key={index}>{key}</li>
            ))}
          </ul>
        </div>
      );
    }
    
    
    ReactDOM.createRoot(
        document.getElementById("root")
    ).render(
        <App />
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
    <div id="root"></div>

    Because react, indeed, needs to reconcile the whole tree if you use indexes and change the order of elements by inserting one in the beginning of the array.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search