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.

Previous:
How to send custom headers in a loader in react-router v7 February 7, 2025 React, JavaScript
Next:
get in JavaScript is the same as property in Python February 13, 2025 Python, JavaScript
Related by category:
Switching from Next.js to Vite + wouter July 28, 2023 React
How to SSG a Vite SPA April 26, 2025 React
An ideal pattern to combine React Router with TanStack Query November 18, 2024 React
How to handle success and failure in @tanstack/react-query useQuery hook September 16, 2024 React
Related by keyword:
4 different kinds of React component styles April 7, 2016 React, JavaScript