1

In below code, state of Test is updating but its not re-rendering. I have updated the state of parent on button click on change of which I expected it to rerender the whole component including Button. But its not re-rendering Button. Need help wrt this. This is a test code and both classes are necessary.

import React from 'react';

class Button extends React.Component {
  constructor(props){
      super(props)
      this.state = {
          id : props.id
      }
  }
  render() {
      console.log('Button state id is', this.state.id)
      return(
          <div>
              'hi ' +  {this.state.id}
              <br/>
              <button type='submit' onClick={this.props.handleSubmit}>
                  submit
              </button>
          </div>
      )
  }
}

export default class App extends React.Component {
  constructor(props) {
      super(props)
      this.state = {
          id: 1
      }
      this.changeId = this.changeId.bind(this)
  }
  changeId() {
      let id = this.state.id
      console.log('parent state id is', id)
      this.setState({
          id: ++id
      })
  }
  render() {
      return(
          <Button id={this.state.id} handleSubmit={this.changeId}/>
      )
  }
}

EDIT: I have modified the code to remove obvious errors like not passing in changeId function to Button

EDIT 2: Found the solution here: React Child Component Not Updating After Parent State Change componentWillReceiveProps

xploreraj
  • 3,792
  • 12
  • 33
  • 51
  • where is this id id={id} – cuongtd Aug 06 '19 at 06:27
  • Did you forget passing handleSubmit={this.changeId} to your – G_S Aug 06 '19 at 06:30
  • `id` is not defined in `changeId()` – Siddhartha Chowdhury Aug 06 '19 at 06:31
  • May not be related to answering this question but worth mentioning here. @xploreraj, this is why I like using TypeScript along with React. It is much clearer as to what is there in our Props. You define props as an interface and declare the properties it is going to have. Same is the case with state. – Raja Malik Aug 06 '19 at 06:40
  • yes, this is just code for this question, it should be `this.state.id` – xploreraj Aug 06 '19 at 07:12
  • Possible duplicate of [React Child Component Not Updating After Parent State Change](https://stackoverflow.com/questions/41233458/react-child-component-not-updating-after-parent-state-change) – xploreraj Aug 06 '19 at 08:02
  • `componentWillReceiveProps` is `react` way! – Vishal Kumar Sahu Mar 16 '21 at 16:14

4 Answers4

3

For a number to re render in the child component you need to make following changes to your code:

In current scenario value of id in changeId function is event, so you can't do ++id. You have to update it to:

changeId() {
    this.setState({
        id: ++this.state.id
    })
}

and for child component to re render the props value, you have to listen if there is any change in props. For that use componentDidUpdate lifecycle of react. Like this:

componentDidUpdate(prevProps){
   if (this.props.id !== prevProps.id) {
    this.setState({id: this.props.id});
  }
}

The other way is don't store props.id in child state. Use it directly in render.

class Button extends React.Component {

  render() {
      return(
          <div>
              'hi ' +  {this.props.id}
              <br/>
              <button type='submit' onClick={this.props.handleSubmit}>
                  submit
              </button>
          </div>
      )
    }
}

class App extends React.Component {
  constructor(props) {
      super(props)
      this.state = {
          id: 1
      }
      this.changeId = this.changeId.bind(this)
  }
  changeId() {
      this.setState({
          id: ++this.state.id
      })
  }
  render() {
      return(
          <Button id={this.state.id} handleSubmit={this.changeId}/>
      )
    }
}

ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
Yogesh Devgun
  • 1,297
  • 1
  • 19
  • 36
  • 1
    You are right. But `componentWillReceiveProps` should be appropriate in my case. – xploreraj Aug 06 '19 at 08:01
  • @xplorerajL: componentWillRecieveProps is deprecated. It will still work but not advisable. [You can read here](https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops). – Yogesh Devgun Aug 06 '19 at 08:10
0

You haven't actually passed handleSubmit as a prop to the Button component. Assuming you want changeId() to be called when you click the button, try this:

class Test extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            id: 1
        }
        this.changeId = this.changeId.bind(this)
    }
    changeId() {
        console.log('change id called', id)
        this.setState({
            id: ++id
        })
    }
    render() {
        return(
            <Button id={id} handleSubmit={this.changeId}/>
        )
    }
}
adam_th
  • 397
  • 3
  • 11
0

We can further optimise the component by doing something like this -: Highlights -:

  • changeId() { changed to changeId = () => { (fat arrow notation), if we use this we don't need this.changeId = this.changeId.bind(this);
  • Don't really need another component specially for button, can combine it in same component.
import React, {Component} from 'react'; 
export default class Test extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                id: 1
            }
        }
        changeId = () => {
            this.setState({
                id: ++this.state.id
            })
        }
        render() {
            return(
               <div>
                    'hi ' +  {this.state.id}
                    <br/>
                    <button type='submit' onClick={this.changeId}>
                        submit
                    </button>
                </div>
            )
        } }
GalAbra
  • 5,048
  • 4
  • 23
  • 42
Abhisar Tripathi
  • 1,569
  • 10
  • 21
0

The example for componentWillReceiveProps worked in my case, when I wanted to update child component of React on setState of parent component:

      componentWillReceiveProps(props) {
        this.setState({
          currentID: props.currentID
        });
      }
Vishal Kumar Sahu
  • 1,232
  • 3
  • 15
  • 27