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.
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.
Comments