9

Okay, I already know one way to do this. However, I am asking this in case I am reinventing the wheel since I am very new to React. I was under the impression that if a parent component passes her state to the child components via props than upon updating the parent's state, the child will re-render if needed. But this does not seem to be the case. I set up this example,

class Child extends Component {
  constructor(props) {
    super(props);
    this.state = {
      number: props.number,
    };
  }

  updateNumber(n) {
    this.setState({number: n});
  }

  render() {
    return (<h1>{this.state.number}</h1>);
  }
}

class Parent extends Component {

  constructor(props) {
    super(props);
    this.state = {
      number: -1,
    };
    this.child = React.createRef();
    setInterval(this.updateState.bind(this), 1000);
  }

  updateState() {
    console.log(this.state);
    this.setState({
      number: Math.floor((Math.random() * 10) + 1),
    });
    // this.child.current.updateNumber(this.state.number);
  }

  render() {
    return (
      <div>
        <Child ref={this.child} number={this.state.number}/>
      </div>
    );
  }
}

In this example, unless I explicitly define a reference and use it to call the child's update function (the commented part), child is not re-rendered each time the parent's state is updated. So is that it? Are you suppose to manually update the state of your children (heh) or should they automatically update (and thus re-render) if their parent's state is passed to them as props.

scribe
  • 673
  • 2
  • 6
  • 17
  • So basically you are trying to update your parent's state in your child component ? – junwen-k Nov 23 '19 at 09:25
  • Well, I’m not sure if I’d put it that way. I pass a parent’s variable’s value to the child. Then child renders that value. Say in future that variable in parent changed values, shouldn’t the child consequently update itself on the screen according to the change in parent. I’m trying to find out if a props are passed by value or reference, if that makes sense? – scribe Nov 23 '19 at 09:31
  • 3
    Its because your Child component is using its own state as well. You should use props.number instead – junwen-k Nov 23 '19 at 09:32
  • 1
    Yes, I needed to use the child's props instead of storing them into their state. I was under the assumption that props are only used to pass around data and everything that is actually used should be stored in the states. – scribe Nov 24 '19 at 02:10

3 Answers3

12

One easy option to re-render a child is to update a unique key attribute every time you need a re-render.

<ChildComponent key={this.state.updatedKey}/>

Gary Vernon Grubb
  • 9,695
  • 1
  • 24
  • 27
10

It is because your Child component is using its own state as well. Always ask yourself if state is necessary, its a common mistake for React beginner.

Hope you can understand it more by looking at this example. Instead of calling setInterval in your constructor function, I will recommend you to call it in componentDidMount and to clearInterval in your componentWillUnmount.

// Since you are not using state, you can use functional component for your Child component
const Child = ({ number }) => <h1>{number}</h1>;

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.timer = null;
    this.state = { number: 0 };
  }

  componentDidMount() {
    // setInterval after component did mount
    this.timer = setInterval(this.incrementNumber, 1000);
  }

  componentWillUnmount() {
    // clearInterval when component is going to unmount
    if (this.timer) {
      clearInterval(this.timer);
    }
  }

  incrementNumber = () => {
    // setState function callback to ensure state received will be latest state
    this.setState(prevState => ({ number: prevState.number + 1 }));
  }

  render() {
    const { number } = this.state;
    return (
      <div>
        <Child number={number} />
      </div>
    );
  }
}
junwen-k
  • 3,454
  • 1
  • 15
  • 28
  • I see, one question though when you set up `setInterval(this.incrementNumber, 1000)` don't you have to bind `this` to the increment function like this `setInterval(this.incrementNumber.bind(this), 1000)` so you can use `this.setState` inside the `incrementNumber`? – scribe Nov 24 '19 at 02:17
  • 1
    @scribe Take a look at [this](https://www.codementor.io/dariogarciamoya/understanding-this-in-javascript-with-arrow-functions-gcpjwfyuc) explanation about using arrow function. This should answer your question – junwen-k Nov 24 '19 at 06:57
2

your approach is wrong dont update you parent state from child like this and don't save props in child state they are already updated as the parent updates.

and if you want to update parent form child pass props form child to parent like this.when parent updates child will update too.

class Child extends Component {
  constructor(props) {
    super(props);
  }

updateNumber=()=>{// update parent form child. call this function in somewhere in your component. if you want to pass event pass it as second argument
  this.props.updateNumber(newUpdatedNumber,event);
}

  render() {
    return (<h1>{this.props.number}</h1>);
  }
}

class Parent extends Component {

  constructor(props) {
    super(props);
    this.state = {
      number: -1,
    };
    this.child = React.createRef();
    setInterval(this.updateState.bind(this), 1000);
  }

  updateState() {
    console.log(this.state);
    this.setState({
      number: Math.floor((Math.random() * 10) + 1),
    });
  }
//update parent from child

  updateParentFromChild=(updatedNumber,event)=>{//catch function from child 
   this.setState({
      number:updatedNumber
   });
  }


  render() {
    return (
      <div>
        <Child ref={this.child} updateNumber={this.updateParentFromChild} number={this.state.number}/>
      </div>
    );
  }
}
Naveen Kashyap
  • 372
  • 1
  • 9
  • I think you misunderstood me a little, to quote you: "your approach is wrong dont update you parent state from child like this". I am not doing that (so I think), my problem was not trying to update the parent from children. However, as you said, " and don't save props in child state they are already updated as the parent updates.", this was my issue. – scribe Nov 24 '19 at 02:08
  • then you just could use this.props.propName in your child component – Naveen Kashyap Nov 24 '19 at 15:15
  • but in some project you'll need to update parent state from child so the answere is here – Naveen Kashyap Nov 24 '19 at 15:16