Reputation: 1077
What are the pitfalls of using an index as key for items in the list? Is there any performance pitfall for React change detection or any unexpected list update while adding or removing elements in the list as well. I have gone through several articles regarding this but still not getting it clear.
Please refer codepen
Why adding an item at the start of the list result in unexpected behavior in the above codepen?
Also, it is said that by default react passes index as a key when no key is passed. That means not passing any key and passing index as a key - both are the same thing?
Upvotes: 26
Views: 47903
Reputation: 1696
Okay, this is the first Stack Overflow result on Google for this issue, and I want to talk about the serious misinformation I see all over the place and attempt to add some clarity.
NEVER use array index as key
- several Twitter tech influencers
How about, NEVER trust anyone who says NEVER in reference to programming. The true answer is (almost) always "it depends".
[you may use index as key if] the list is never reordered or filtered.
- Robin Pokorsky's article
Hmm, really? This seems to work just fine:
const [list, setList] = useState([3, 2, 4, 1]);
return (
<>
<button onClick={() => setList([0, ...list])}>Unshift</button>
<button onClick={() => setList([...list, 0])}>Push</button>
<button onClick={() => setList([...list].sort())}>Sort</button>
<ul>{list.map((item, index) => (<li key={index}>{item}</li>))}</ul>
</>
);
Yes, with setList
I'm re-setting the entire list instead of mutating, but that has always been what you're supposed to do with array/objects in useState
, props, etc.
Performance wise i don't think it makes any difference
Not quite true. The React docs say it does. See my discussion below.
If the list is static (no sorting or filtering happening), then you can just use the array index. I think index can also be used, as long as there's no focusable items or input elements.
- Reddit comment
Ummm... not quite. This is a good example of how information gets misconstrued when blindly quoting Medium articles. They probably got the "focusable or input elements" from Robin Pokorny's beginning example and took the wrong lesson from it. See my discussion below.
So when u just use indexes as key and try to change the filter to 'Seconds' or 'Hours', the react will look at the data and it will assume the data have not changed and will not rerender the list. Inorder to achieve that, we have use unique keys even for the filters.
- answer on this question
Not true at all, unless you're talking about something very different. I really don't know where you got this idea. This works fine:
const [minutes, setMinutes] = useState(false);
const [list, setList] = useState([
{ name: "riderOne", time: 10 },
{ name: "riderTwo", time: 11 },
{ name: "riderThree", time: 12 },
]);
return (
<>
<button
onClick={() => setList([{ name: "riderZero", time: 5 }, ...list.reverse()])}
>
insert
</button>
<button onClick={() => setMinutes(true)}>in minutes</button>
<ul>
{list.map((item, index) => (
<li key={index} data-test={item.time / (minutes ? 60 : 1)}>
{item.time / (minutes ? 60 : 1)}
</li>
))}
</ul>
</>
);
can cause data-dom state mismatch
- various articles
How? When? What specific conditions cause this? I've been using React professionally for many years and haven't run into data mismatch error pretty much ever (except in cases where I was accidentally not passing a unique index, like in nested for loops).
You should just get into a habit of not using index.
- various people on internet
How about a habit of actually understanding why something needs to be a certain way, or using the appropriate tool for the job?
I'm really tired of people throwing around absolutes, which get quoted and passed around in a game of telephone with only a surface-level understanding of the "why".
The new React docs talk about not using index as a key, but don't do anything to explain why.
The old docs have what I think are the two most salient points, said more explicitly:
This can work well if the items are never reordered, but reorders will be slow.
As a result, component state for things like uncontrolled inputs can get mixed up and updated in unexpected ways.
Emphasis mine. These are the key points.
My code examples above work just fine (in terms of the true state of the data being represented in the dom accurately, in the right order), but it might cause "performance issues". Having a unique/stable key helps React be more efficient with DOM updates which inserting/removing/re-ordering items in the list. I think this article does a pretty good job of explaining the "why" behind this (but also says there could be issues with data mapping/binding and completely fails to explain why/how/when).
All of that said, you should always be skeptical when someone tells you you must do something for performance reasons. If you are just rendering a todo list of 10 plain-text items, I guarantee that even on a 10 year old smartphone the difference will not be humanly perceivable. If you're rendering hundreds or thousands of list items, and/or complex list items with many DOM children, then it might make a difference. But actually test to verify that it does! I'd wager that you've already spent more time reading about this issue than the theoretical total of React re-render time-savings over the whole lifetime of your app.
To its credit, the Robin Pokorny article starts right away with an example of one (and possibly only?) common situation where data binding could be an issue. But, again, it doesn't explain why.
The example fails because the inputs are uncontrolled, not because they are inputs or focusable. That is, the browser is keeping track of its state, not React. So when updating the DOM to insert another text input before it, React sees that there's an input element already there and just leaves it alone.
As such, I think the data-binding warning can be expanded to: "uncontrolled elements or anything that is otherwise outside of React's awareness". That could be a <input>
/<select>
/<textarea>
, but also elements rendered through other means like third party libraries (e.g. D3 visualizations) or direct dom manipulation (e.g. vanilla js).
If I had to sum up, my rule of thumb would be this.
It is okay to use a for loop index
as a key
, unless one or more of the following is true:
key
results in a noticeable performance improvement (this should really only happen if list items can be inserted/removed/re-ordered† AND you're rendering many and/or complex items).<input>
/<select>
/<textarea>
elements in your list items AND list items can be inserted/removed/re-ordered.† In fact, as of writing, if you insert/remove, React only seems to update the DOM nodes of the items that are after it. But that does not tell the full story. The exact details of the extra work that might need to be done in other parts of the process like VDOM diffing are only understood (at a deep, fundamental level) by a handful of people in the world. And, the React team makes under-the-hood optimizations and adjustments to its rendering process all the time. So, instead of speculating and theorizing, just do actual tests/measurements in your specific application.
†† A third party library could be mutating your objects without your knowledge, for example d3-force.
But don't take my word for it, investigate what I'm saying for yourself and verify that it makes sense! And don't quote things out of context without understanding!
Upvotes: 3
Reputation: 157
Let's say you are rendering this array:
const data = [{
name: 'riderOne',
time: 10, // let's assume it's timestamp
},{
name: 'riderTwo',
time: 11, // let's assume it's timestamp
},{
name: 'riderTwo',
time: 12, // let's assume it's timestamp
}];
Now let's say we have a filter with which user can choose the time taken to finish the race can be shown in 'Seconds', 'Minutes', and 'Hours'. Minutes may be the default filter. So when u just use indexes as key and try to change the filter to 'Seconds' or 'Hours', the react will look at the data and it will assume the data have not changed and will not rerender the list. Inorder to achieve that, we have use unique keys even for the filters.
key={`${item.id}-${filter}}
Upvotes: 2
Reputation: 1572
this question has been asked before,
but the main answer could be found in the Docs of React
We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state. Check out Robin Pokorny’s article for an in-depth explanation on the negative impacts of using an index as a key. If you choose not to assign an explicit key to list items then React will default to using indexes as keys.
there are no unexpected list update while adding or removing elements
but the main reason for this is the algorithm for indexing and comparing behind,
you can read about this here under 'different-types'
The key here is to understand not everything in the DOM has a representation in React "Virtual DOM" and, because direct manipulations of the DOM (like a user changing an value or a jQuery plugin listening to an element) are unnoticed by React, not using unique and constant keys will end up with React recreating the DOM node of a component when the key is not constant (and losing any untracked state in the node) or reusing a DOM node to render another component when the key is not unique (and tying its state to this other component).
Here you have a live demo showing how awful the results are
Just add an item, change it, add more items and see what happens.
have a read here too
Upvotes: 24
Reputation:
See the React documentation about lists and keys, and why they are important:
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.
It also says some things about using the index as the key, and why you should not use it:
We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state. Check out Robin Pokorny’s article for an in-depth explanation on the negative impacts of using an index as a key. If you choose not to assign an explicit key to list items then React will default to using indexes as keys. Here is an in-depth explanation about why keys are necessary if you’re interested in learning more.
Upvotes: 2
Reputation: 3226
If the key is an index, reordering an item changes it. Hence, the component state can get mixed up and may use the old key for a different component instance. Without a unique key, React can't differentiate if the element was removed or just the content is changed. As soon as you re-order or filter an array, index is no longer unique.
Upvotes: 1
Reputation: 92
You are right, there can be issues with using index as key but notice I am saying we can get issues but not always. If we are not adding /removing items from list then it is fine to use index as keys else it will be good to use some id which uniquely identifies the item. Reason is if you add or remove some items from the list, indexes change for older items and react can get confused which items are changed. Performance wise i don't think it makes any difference
Upvotes: 6