5

I have a form that contains several questions. Some of the questions are having a set of sub-questions. These are rendered using the following code.

    {
  Object.keys(this.props.moduleDetails.questions).map((questionInfo, index) => (
    <div key={index}>
      <QuestionAnswers
        questionInfo={this.props.moduleDetails.questions[questionInfo]}
        generateStepData={this.props.generateStepData}
        states={this.props.states}
        userEnteredValues={this.props.formValues}
        errors={this.props.errors}
        setQuestionAnswers={this.setQuestionAnswers}
      />
      {this.props.moduleDetails.questions[questionInfo].question_group &&
      this.props.moduleDetails.questions[questionInfo].has_grouped_questions ===
        1 ? (
        <div className="sub-questions">
          {this.props.moduleDetails.questions[questionInfo].question_group ? (
            <span>
              {this.renderQuestionGroup(questionInfo)}
              <input
                type="button"
                onClick={e => {
                  this.addQuestionGroup(questionInfo)
                }}
                value="Add"
                className="btn"
              />
            </span>
          ) : null}
        </div>
      ) : null}
    </div>
  ))
}

As you can see, the question groups are rendered using the renderQuestionGroup(questionInfo) method.

renderQuestionGroup(questionInfo) {
    let input = [];
    this.state.groupedQuestions[questionInfo] = (this.state.groupedQuestions[questionInfo]) ?
                    this.state.groupedQuestions[questionInfo]
                    : [];
    let answerId = this.props.formValues[questionInfo];
    let groupedQuestions = this.props.moduleDetails.questions[questionInfo].question_group[answerId];
    if((groupedQuestions != null && this.state.groupedQuestions[questionInfo].length == 0) || this.state.isAddPressed) {
        this.state.groupedQuestions[questionInfo].push(groupedQuestions);

    }
    input = this.display(questionInfo)
    return input;
  }

The problem here is that I cannot set state inside this method, as this method is called inside render method

Any idea on how to fix this?

Is there any alternate approach for this?

2 Answers 2

11

You can not set state inside render function because it will cause side effect.

What exactly happens is that each time you update state react calls render function, so if you will update state inside render function then it will stuck inside infinite loop.

In general, this should never happen; you should not be doing anything remotely related to modifying any component's state within a render function.

Sign up to request clarification or add additional context in comments.

Comments

7

You need to move your set state logic to

 componentDidUpdate(prevProps, prevState, snapshot) {
        if (prevPros.prop !=== this.props.prop) {
          /* your setState logic*/
        }
      }

And then just use modified state in renderQuestionGroup.

Also never modify state directly with:

this.state.groupedQuestions[questionInfo] = (this.state.groupedQuestions[questionInfo]) ?
                this.state.groupedQuestions[questionInfo]
                : [];

Use setState({groupedQuestions: [...groupedQuestions,updatedGroup })

from react set state docs:

NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.

7 Comments

componentDidUpdate is a better place.
Agree, always forgettind about will methods deprecation
As you can see, each question is passed in the renderQuestionGroup method. So how do i use the componentDidUpdate? Only in the render method, i get each question. Any idea?
you already have new props in componentDidUpdate method. From them you can get all your question objects and update state if needed. There no difference in props objects that passed in render or componentDidUpdate
Move all logic from renderQuestionGroup to componentDidMount and in renderQuestionGroup simple get display-ready data from already modified state
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.