maze
maze

Reputation: 2284

Change checked attribute of controlled checkbox (React)

I am new to React and am probably lacking the correct terminology to find a solution to my problem. It cannot be that hard.

I am building a simple app which displays a set of questions, one question at a time. After answering one question, the next question is shown.

I have a component Question that renders 3 checkboxes, each checkbox represents one possible answer to the Question.

{this.props.question.answers.map((answer, index) => {
  return (
    <li className="Question__answer" key={answer.id}>
      <label className="Question__answer-label">
        <input
          className="Question__answer-checkbox"
          type="checkbox"
          value={index}
          onChange={this.props.setAnswer}
          defaultChecked={false} />
        {answer.answer}
      </label>
    </li>

    ...

    <button className="Question__next" type="button" onClick={this.props.onNext} disabled={this.props.isDisabled}>
        Next question
      </button>
  )
})}

Inside my main component Quiz I call the component like this:

<Question step={this.state.step} question={this.state.questions[this.state.step]} setAnswer={this.setAnswer} onNext={this.onNext} isDisabled={this.isDisabled()} />

onNext is my function which increments this.state.step in order to display the next question:

onNext(event) {
    this.setState({step: this.state.step + 1});
}

Everything works fine, except: When the next question is displayed, I want all 3 checkboxes to be unchecked again. Currently they remember their checked state from the previously answered question.

Upvotes: 3

Views: 21283

Answers (2)

Shubham Khatri
Shubham Khatri

Reputation: 281734

So the thing is that React renders its components such that only the components that changed are rendered. Since nothing has changed about the checkboxes checked attribute after rerendering they stay in their previous state.

What you should do is toggle the checkbox state on click in the setAnswer function and pass this as a prop to the Question component.

Something like

this.state {
   checkedAttr: ["false", "false", "false"]
} 

Pass it on like

<Question step={this.state.step} question={this.state.questions[this.state.step]} setAnswer={this.setAnswer} onNext={this.onNext} isDisabled={this.isDisabled()} checkedAttr={this.state.checkedAttr} />

Use them in the checkboxes state as

{this.props.question.answers.map((answer, index) => {
  return (
    <li className="Question__answer" key={answer.id}>
      <label className="Question__answer-label">
        <input
          className="Question__answer-checkbox"
          type="checkbox"
          value={index}
          checked={this.props.checkedAttr[index]}
          onChange={this.props.setAnswer}
          defaultChecked={false} />
        {answer.answer}
      </label>
    </li>

    ...

    <button className="Question__next" type="button" onClick={this.props.onNext} disabled={this.props.isDisabled}>
        Next question
      </button>
  )
})}

In the onNext function just reset the states checkedAttr to false.

Upvotes: 2

Mani Shooshtari
Mani Shooshtari

Reputation: 780

On the Question Component use the componentWillReceiveProps method like this:

componentWillReceiveProps: function(nextProps) {
  this.setState({
    likesIncreasing: nextProps.likeCount > this.props.likeCount
  });
}

According to the docs :

componentWillReceiveProps will be Invoked when a component is receiving new props. This method is not called for the initial render.

fetch the received step from the Question component and update its state to show the new Question.

Upvotes: 0

Related Questions