Home > Back-end >  React - State change of parent does not cause re-rendering of child when passed down as props?
React - State change of parent does not cause re-rendering of child when passed down as props?

Time:10-05

I have been trying to learn React lately and this issue has really confused me. Here's the code:

I have created two class components: App, representing the parent and Child, representing the child.

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      myState: 0
    }
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    this.setState((previousState) => ({myState: previousState.myState   1}), () => console.log(this.state.myState))
  }

  render() {
    return (
      <div>
        <Child value={this.state.myState}/>
        <button onClick={this.handleClick}>This updates the state of App (parent)</button>
      </div>
    )
  }
}

class Child extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: this.props.value
        }
        this.randomize = this.randomize.bind(this)
    }

    randomize() {
        this.setState({value: Math.random() * 100})
    }

    render() {
        return(
            <div>
                <h1>My value is {this.state.value}</h1>
                <button onClick={this.randomize}>This changes the state of Child</button>
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

The App component contains a button which increments its state value by 1 every time it is clicked. The Child component contains a button which changes its state value to a random number between 0 and 100 every time it is clicked.

While running this, I expected the Child to be re-rendered every time myState in the parent changed, since that is what I passed down to it as props. I also expected this.state.value in the Child to change because I set it to be myState. However, when I incremented myState, the Child is completely unaffected and still displays whatever randomize() gave to it before.

Can anyone show me what's wrong? Thanks :)

CodePudding user response:

You have the following snippet in your Child constructor:

this.state = {
  value: this.props.value
}

The above only sets the Child state once, before the component is mounted. Therefore, none of your increments/decrements push through from your parent to child.

The best solution to your problem is to simply keep the state in your parent component, and only pass down a function that can update it.

Try this:

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      myState: 0
    }
    this.handleClick = this.handleClick.bind(this)
    this.updateState = this.updateState.bind(this)
  }

  updateState(newState) {
    this.setState(newState);
  }
 
  handleClick() {
    this.setState((previousState) => ({myState: previousState.myState   1}), () => console.log(this.state.myState))
  }

  render() {
    return (
      <div>
        <Child value={this.state.myState} update={this.updateState}/>
        <button onClick={this.handleClick}>This updates the state of App (parent)</button>
      </div>
    )
  }
}

class Child extends React.Component {
    constructor(props) {
        super(props)
        this.randomize = this.randomize.bind(this)
    }

    randomize() {
      this.props.update({myState: Math.random() * 100})
    }

    render() {
        return(
            <div>
                <h1>My value is {this.props.value}</h1>
                <button onClick={this.randomize}>This changes the state of Child</button>
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

CodePudding user response:

You are missing component lifecycle. constructor of a class called once only, not on every re-render.
If u wants to change the data of child wrt parent, then use componentDidUpdate(), which will get called whenever data is updated. refer here
Modified Code for child:

class Child extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: this.props.value
        }
        this.randomize = this.randomize.bind(this)
    }
    componentDidUpdate(prevProps) {
        if (this.props.value !== prevProps.value) {
            this.setState({value: this.props.value})
        }
    }
    randomize() {
        this.setState({value: Math.random() * 100})
    }

    render() {
        return(
            <div>
                <h1>My value is {this.state.value}</h1>
                <button onClick={this.randomize}>This changes the state of Child</button>
            </div>
        )
    }
}
  • Related