I'm sure you've seen this React code:


<ul>
  {users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>

The key prop is necessary so each element is identifiable.

But did you know you can pass key to a component even though there's no .map(...) or other array of React elements.

Consider this simplified app:


function Form({ user }: { user: User }) {
  const [comment, setComment] = useState("");
  return (
    <form>
      <label htmlFor="comment">User comment for '{user.name}':</label>
      <textarea
        id="comment"
        aria-label="User comment"
        value={comment}
        onChange={(event) => setComment(event.target.value)}
      />
    </form>
  );
}

And you use it like this:


<Form user={currentUser} />

The problem is; if that currentUser changes in consequent renders, it won't reset that const [comment, setComment] = useState("") state. Perhaps that's not desired, but it's not implausible that you'd want, as per this simple example, a different comment for different users.

To achieve that, add the magical prop key:


<Form user={currentUser} key={currentUser.id} />

Now, that const [comment, setComment] = useState("") state, inside the <Form> component, will "reset" as the currentUser.id changes.

Full Codesandbox example here

An alternative solution

In the above example, the <Form> component completely "starts over" when the key={...} prop changes. What if the <Form> component depends on a bunch of other props such as jokeOfTheDay={joke} writingStyle={style} {...etc}.

You might not want to reset the const [comment, setComment] = useState("") state just because the writingStyle={...} prop changes.

To accomplish this, you can keep the user prop as a piece of state. Like this:


const [previousUser, setPreviousUser] = useState(user);

And when it changes, you apply whatever resets (state updates) you need. Like this:


const [previousUser, setPreviousUser] = useState(user);
if (user !== previousUser) {
  setPreviousUser(user);
  setComment("");
}

This pattern also has the advantage that, from within the component, you don't need to tell the caller of it what it should do.

Full Codesandbox example here

Comments

Your email will never ever be published.

Related posts