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!