# About React Keys

This is mostly a rephrased version of React's official documentation on [Lists and Keys](https://reactjs.org/docs/lists-and-keys.html#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.

```jsx
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.

```jsx
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`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md) 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:

```python
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`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md) 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.

```python
function Example() {
  const [nonce, setNonce] = useState(Date.now());

  return <div>
    <Button onClick={() => setNonce(Date.now())} />
    <Universe key={nonce} />
  </div>
}
```
