About React Keys

Software Engineer @ Memfault, Ex-Fitbit, Ex-Pebble
This is mostly a rephrased version of React's official documentation on Lists and Keys with some additional examples.
React allows specifying a key property on a component. It lets React identify components and track their identities.
Iterating over Arrays
Imagine we have a stateful component EditableFruit that also has side-effects.
function EditableFruit({ value: initialValue }) {
const [value, setValue] = useState(initialValue);
useEffect(() => console.log("init", initialValue)); // "once" effect
return <div>
<label>{initialValue}</label>
<input value={value} onChange={e => setValue(e.currentTarget.value)} />
</div>
}
And a container that uses this component.
function Example() {
const fruits = ["APPLE", "BANANA"];
return <div>
{fruits.map((fruit, index) => <EditableFruit key={SEE_BELOW} value={fruit} />)}
</div>
}
We have a few options what to use for key.
Using a unique key
If each element has some key that is guaranteed to be unique (in this case fruit itself), always use that. When the order of the array changes, React will be able to re-associate the DOM elements and React components with the array items.
Adding an AVOCADO in the middle will log init AVOCADO. Everything "works" as one might expect.
Using the index
If no unique key exists, using the index might be the only option.
When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort.
However it means React won't be able to semantically identify elements which can lead to bugs, especially when the components contain state or effects.
Adding an AVOCADO in the middle will log init BANANA. The rendered inputs will contain the values APPLE, BANANA (!), and BANANA.
Because of this, the React documentation states that using index should be a last resort.
The ESLint rule react/no-array-index-key can detect this, forcing you to add an ignore if one really want to accept this risk. In the current codebase I'm working on (which is using TypeScript) we can use key={index!} to silence the linter.
Using the index joined with a (potentially) unique key
Using a key like ${fruit}-${index} will avoid the issue where state might be associated with the wrong component when just using index. However, this is a heavy shotgun comes at the cost of performance.
Adding an AVOCADO in the middle will log init AVOCADO and init BANANA. This makes sense when you look at what React has to do to determine the changes:
diff({
from: ["APPLE-0", "BANANA-1"],
to: ["APPLE-0", "AVOCADO-1", "BANANA-2"]
}) // { added: ["AVOCADO-1", "BANANA-2"], removed: ["BANANA-1"] }
There are situations where this might be acceptable, but I would be careful when performance is critical, especially when the array is large.
Not providing a key
Not providing a key is effectively the same as passing index.
The ESLint rule react/jsx-key can detect this forcing you to make a conscious choice.
Resetting a component's state
I sometimes find myself using key as a quick (and dirty) way to reset the state of a component, where I really want a big hammer.
function Example() {
const [nonce, setNonce] = useState(Date.now());
return <div>
<Button onClick={() => setNonce(Date.now())} />
<Universe key={nonce} />
</div>
}



