0

I am new to react js. This might be an easy question but I am not getting a way to do this. So, I am asking this question.

I have a grandParentComp which is like

GrandParent

import React from 'react';

class QuizSetupMain extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            technologies: this.props.technologies,
            hasError: false
        }
    }
render() {
        return (

            <div >
            {this.state.technologies.length > 0 ? <LowLevelCriteria techData={this.state.technologies} /> : null}

    <div className="fetchBtnDiv mx-auto">
                    <button className="btn btn-primary fetchBtnSize" disabled={this.state.hasError}>Fetch Questions</button>
                </div>
</div>

Herem, I have button which I want to disable on the basis of the state variable

LowCriteria.js this is the child

import React from 'react';

class LowLevelCriteria extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            technologies: this.props.techData,
            lowData: this.props.lowData.low,
            hasError: false
        }
    }

  componentWillReceiveProps(nextProps) {
        if (nextProps.lowData.Low.length > 0) {
            console.log("in this");
            var previous_data = nextProps.lowData;
            var i = 0;
            previous_data.Low.map((object) => {
                i = i + parseInt(object.numberOfQuestions);
            });
            console.log("value of i is ==>", i);
            if (i >= 6) {
                this.setState({
                    hasError: true
                })
            } else {
                this.setState({
                    hasError: false
                })
            }
        }
    }

  render() {
        return (
            <div>
                <div className="questionLevelIndication">
                    <span className="levelIndicatorBtn backgroundColorForLow">
                        1
                    </span>
                    <label className="levelIndicationLabel">
                        Low Difficulty Level - Maximum 6 questions
                    </label>
                    {this.state.hasError && <div class="alert alert-danger alert-dismissible fade show">
                        <button type="button" class="close" data-dismiss="alert">&times;</button>
                        <strong>Please reduce number of questions</strong>
                    </div>}
                </div>
                {(this.props.lowData) && this.props.lowData.Low.length > 0 && this.props.lowData.Low.map(data => (
                    <LowRow technologies={this.state.technologies} hasError={this.state.hasError} onChange={this.onchange.bind(this)} data={data} key={data.id} onAddRow={this.onaddRow.bind(this)} onRemoveRow={this.onRemoveRow.bind(this)} />
                ))}
            </div>
        )
    }

So, Now I want to pass this this.state.hasError to the parent , so that I can disable the button which is on the parent component.

I dont want to use the redux.

Can any one help me with this ?

I don't have any button or function that is going to get called from the child function .so that I can update the parent state.

Ahmet Emre Kilinc
  • 5,489
  • 12
  • 30
  • 42
ganesh kaspate
  • 1
  • 9
  • 41
  • 88
  • Do you mean `grandchild to parent`? Or `grandchild to grandparent`? There's a difference – StudioTime Oct 02 '18 at 12:55
  • Sorry Actually from child to parent. – ganesh kaspate Oct 02 '18 at 12:56
  • In my case from LowCriteria to the GrandParent – ganesh kaspate Oct 02 '18 at 12:57
  • There is no way to pass a state from a child to a parent in React. – Cata John Oct 02 '18 at 12:57
  • Instead of passing down a value, you could instead pass down a function `setError` or something which is implemented in the grandparent component. The grandchild can then just call `this.props.setError(True)` or something when an error occurs. – Aquarthur Oct 02 '18 at 12:58
  • Possible duplicate of [How to pass state back to parent in React?](https://stackoverflow.com/questions/40722382/how-to-pass-state-back-to-parent-in-react) – StudioTime Oct 02 '18 at 12:58
  • Okay , so Not using state or anything other than this ? – ganesh kaspate Oct 02 '18 at 12:58
  • @DarrenSweeney Actually, I don't have any button or action that is going to get called from the child so that I can update the parent state. that's why I asked this – ganesh kaspate Oct 02 '18 at 12:59
  • You didn't mention the React version, in recent versons you can use Context to connect component and stay decoupled. [Here](https://reactjs.org/docs/context.html) the documentation. – Mario Santini Oct 02 '18 at 13:03
  • Just move your state up to the grandparent component and pass down a function that will modify the hasError value from your grandparent component to your child component – MEnf Oct 02 '18 at 13:09
  • Your data is supposed to only flow down. It's bad practice to move state upwards in React. – MEnf Oct 02 '18 at 13:10

2 Answers2

0

You can't update the parent's state directly from the child component. Your best bet would be to create a handler function that's passed down to the child that lives on the parent that updates the parent's state.

Katia Wheeler
  • 519
  • 3
  • 8
  • But I dont't have any function in the child which is going to get called on the change of that data. – ganesh kaspate Oct 02 '18 at 13:18
  • Try moving the button to the child component. Then pass a function from the parent called `handleFetchQuestions` or something that deals with any API call (assuming from the name) that then updates the parent's state (`technologies`) based on the result. Technologies is being passed to the child anyways, so once you handle the fetching of questions in the parent and set its' new state, it will be passed to the child again. Then, you can deal with the error state directly within the child component and don't have to worry about passing anything up to the parent. – Katia Wheeler Oct 02 '18 at 13:21
0

You need a callback function to handle this. Do as suggested below

In your GrandParent component declare handler function inside the component and set the flag to hasError. This flag is a Boolean which you will receive from a callback function from child component

 handleHasError = flag => {
      this.setState({
          hasError: flag
      });
 }

Pass down handleHasError to LowLevelCriteria like

     {this.state.technologies.length > 0 ? <LowLevelCriteria techData={this.state.technologies} handleHasError={this.handleHasError}/> : null}

In your LowLevelCriteria component call this.props.handleHasError function like below

  if (i == 6) {
            this.setState({
                hasError: true
            })
           this.props.handleHasError(true);
        } else {
            this.setState({
                hasError: false
            })
          this.props.handleHasError(false);
        }

With this change the button will work as you expected

Edit:

You getMaximum update depth exceeded because you are not comparing current props with nextProps. You are directly doing setState so you need to Compare them and then do setState

  componentWillReceiveProps(nextProps) {
     if(nextProps.lowData != this.props.lowData){
    if (nextProps.lowData.Low.length > 0) {
        console.log("in this");
        var previous_data = nextProps.lowData;
        var i = 0;
        previous_data.Low.map((object) => {
            i = i + parseInt(object.numberOfQuestions);
        });
        console.log("value of i is ==>", i);
        if (i == 6) {
            this.setState({
                hasError: true
            })
        this.props.handleHasError(true);
        } else {
            this.setState({
                hasError: false
            })
        this.props.handleHasError(false);
        }
    }
   }
  }

Please excuse me if there are any typo errors because I am answering in my mobile

Hemadri Dasari
  • 32,666
  • 37
  • 119
  • 162