Don't Forget Your Keys!
TL;DR: React will re-use an instance of a component (sharing state in the process) if you do not set the key
attribute to differentiate identical components in the same DOM position.
Context:
I was trying to render a list of QuestionCard elements for my trivia web app, but when a new question was rendered, it would have the button state from the previous card. Initially, I thought I had made a mistake with props, but it was actually React’s reconciliation, where React will reuse an instance of a component if it can.
Spoiler alert: The fix is to force React to reset state in one of two ways. Per the React docs:
There are two ways to reset state when switching between them:
- Render components in different positions
- Give each component an explicit identity with key
- You can force a subtree to reset its state by giving it a different key
— React.dev - Preserving and Resetting State
Enter key
(more play on words, sorry)
I needed to render new question cards in the same DOM position to keep things tidy, so I went the key
route, like so:
// React key to differentiate cards
key={props.index}
Before
Rendering a list of QuestionCard elements (sans index
prop):
const questionCards: ReactElement[] = props.questions?.map((questionData) => (
<QuestionCard
questionData={questionData}
incrementIndex={incrementIndex}
/>
));
After
Passing index
prop and setting it inside of QuestionCard:
const questionCards: ReactElement[] = props.questions?.map((questionData) => (
<QuestionCard
index={index}
questionData={questionData}
incrementIndex={incrementIndex}
/>
));
JSX inside of QuestionCard:
return (
<TrueOrFalseCard
incrementIndex={props.incrementIndex}
key={props.index}
questionData={props.questionData}
/>
);
I’m pretty sure I’ve hit this problem before, so I decided to write a quick blurb on the “why” behind it, so I’d be less likely to forget next time. Hopefully it was helpful!