# Importance of React Keys

### Introduction

We are going to explore the importance of using keys effectively as well as going a tiny bit in depth with how react handles re-rendering.

### The context

I thought I knew how React components get updated until I came across an example similar to this:

{% code fullWidth="true" %}

```jsx
function ChatLobby() {
  const [selectedUser, setSelectedUser] = useState("Timothee");

  return (
    <div>
      <header>
        <button onClick={() => setSelectedUser("Timothee")}>
          Timothee
        </button>
        <button onClick={() => setSelectedUser("Adam")} >
          Adam
        </button>
      </header>
      
      <section>
        {selectedUser === "Timothee"
          ? <Chat with="Timothee" />
          : <Chat with="Adam" />}
      </section>
    </div>
  )
}

function Chat(props) {
  const [text, setText] = useState("");
  
  return (
    <div>
      <h4>Write a message to <span>{props.with}</span></h4>
      <input value={text} onChange={e => setText(e.target.value)} />
      <button>Send</button>
    </div>
  )
}
```

{% endcode %}

{% embed url="<https://streamable.com/dvxa90>" %}
Extremely simple chat lobby markup
{% endembed %}

{% hint style="info" %}
I've stripped away the styling from the code above to focus on the topic at hand.
{% endhint %}

We have a chat lobby where we can select the user to whom we want to send a message.\
Everything seem to work fine, but we have a small issue, reproduced like this:

1. Type a message to Timothee
2. Don't press the send button
3. Select Adam

The message persists even though we expected the input to clear itself.

You can try it out [here](https://stackblitz-starters-7jomxs.stackblitz.io) and see the source code [here](https://stackblitz.com/edit/stackblitz-starters-7jomxs?file=src%2Findex.js).

### Let's see what's going on

We inspect the code again and notice how `Chat` has an internal `text` state that has the initial value an empty string.

<pre class="language-jsx" data-full-width="true"><code class="lang-jsx">function Chat(props) {
<strong>  const [text, setText] = useState("");
</strong>  // ...
}
</code></pre>

Additionally, `ChatLobby` appears to conditionally render two instances of the same `Chat` component.

<pre class="language-jsx" data-full-width="true"><code class="lang-jsx">&#x3C;section>
<strong>  {selectedUser === "Timothee"
</strong><strong>    ? &#x3C;Chat with="Timothee" />
</strong><strong>    : &#x3C;Chat with="Adam" />}
</strong>&#x3C;/section>
</code></pre>

Intuitively, we expect the chats with Timothee and Adam to have separate states, so switching between them should clear the input.

### Can you spot the problem?

I'm ashamed to admit that I haven't spent too much time reading about it in the React docs prior to this.&#x20;

> React will keep the \[component] state around for as long as you render the same component at the same position in the tree.

Here's the answer!\
After the condition gets resolved, we'll end up with the same Virtual DOM structure, so React has no reason to treat it as a new different component.

Syntactically, it appears as two different components, but semantically, you can imagine it has the same *effect* as this:

<pre class="language-jsx" data-full-width="true"><code class="lang-jsx">&#x3C;section>
<strong>  &#x3C;Chat with={selectedUser} />
</strong>&#x3C;/section>
</code></pre>

Here, it is very easy too see why React would keep the state between renders since the only thing that is changed is the `selectedUser`.

{% hint style="warning" %}
One might naively try to solve the problem by manually resetting the state whenever the `with` prop changes, like this:

<pre class="language-jsx" data-title="Chat.jsx" data-full-width="true"><code class="lang-jsx">function Chat(props) {
  const [text, setText] = useState("");
  
<strong>  useEffect(() => {
</strong><strong>    setText("");
</strong><strong>  }, [props.with]);
</strong>  // ...
}
</code></pre>

This will lead to a lot of confusion and unnecessary complexity down the line as the project grows larger.

Also, you might want to check out [this amazingly written article](https://react.dev/learn/you-might-not-need-an-effect) in the React docs.
{% endhint %}

### How to avoid the problem?

Going back to the previous example, we have multiple options to avoid this problem from happening.

#### Use key prop

<pre class="language-jsx" data-full-width="true"><code class="lang-jsx">&#x3C;section>
  {selectedUser === "Timothee"
<strong>    ? &#x3C;Chat key="Timothee" with="Timothee" />
</strong><strong>    : &#x3C;Chat key="Adam" with="Adam" />}
</strong>&#x3C;/section>
</code></pre>

We explicitly tell React these two should be treated as new instances and we can see this happening by detecting whether `Chat` is mounted or not using `useEffect`. This did not happen with our first example, React nerver remounted `Chat` after the initial mount.

{% hint style="info" %}
Try to log whenever the `Chat` component is being mounted and check the console while switching between Timothee and Adam.

You can read more about React mount lifecycle [here](https://react.dev/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means).
{% endhint %}

Given that we use the same component, if we don't provide a key, React uses the component's internal index, which is equivalent to its position, hence why the component was being treated the same.

#### Split the condition

<pre class="language-jsx" data-full-width="true"><code class="lang-jsx">&#x3C;section>
<strong>  {selectedUser === "Timothee" &#x26;&#x26; &#x3C;Chat with="Timothee" />}
</strong><strong>  {selectedUser === "Adam" &#x26;&#x26; &#x3C;Chat with="Adam" />}
</strong>&#x3C;/section>
</code></pre>

In this scenario, as opposed to what we had up until now, we have **two** `ReactNode`s instead of one being evaluated at a time since both conditions are resolved either into a `Chat` or a boolean `false`. It's just that we don't see the boolean node because it is being treated as an `EmptyNode` and does not end up in the DOM.

To clarify it further, if we select Timothee, we end up with a structure like:

{% code fullWidth="true" %}

```json
{ 0: <Chat with="Timothee" />, 1: false }
```

{% endcode %}

and when we select Adam:

{% code fullWidth="true" %}

```json
{ 0: false, 1: <Chat with="Adam" /> }
```

{% endcode %}

Here, React can't confuse them as being the same, since they have a different internal index, even if we don't provide a key.

We can also see this being put in action in the React's [source code](https://github.com/facebook/react/blob/3d6fcf958233a004ff9bd0af8f1f65b8f22f1b3c/packages/react-reconciler/src/ReactChildFiber.js#L334):

<pre class="language-typescript" data-full-width="true"><code class="lang-typescript">let existingChild: null | Fiber = currentFirstChild;
while (existingChild !== null) {
  if (existingChild.key !== null) {
<strong>    existingChildren.set(existingChild.key, existingChild);
</strong>  } else {
<strong>    existingChildren.set(existingChild.index, existingChild);
</strong>  }
  existingChild = existingChild.sibling;
}
</code></pre>

where we see that React is either using the `index` (position) or the `key` to identify the component (named as `existingChild`).

### Conclusion

I want to leave you with this simple rule of thumb:\
If the components conceptually should differ from one another, mark them with a key! :tada:

*This topic has already been* [*explained in much more detail in the React docs*](https://react.dev/learn/preserving-and-resetting-state) *and this article serves as sort of a summary alongside my own thoughts.*
