Share state between childs in react with objects

I'm having some problems when I try to update all childs states from one of the child, here is an example of my code. The idea is to autoupdate all components from one of them.

I'm new in react, I have only been using for a week, so probably all this is a misunderstanding.

https://codesandbox.io/s/430qwoo94

import React from 'react';
import { render } from 'react-dom';
import Hello from './Hello';

class Parent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filedStr: 'some text',
      fieldObj: {
        field1: true,
        field2: true
      }
    }
      }

  updObj = (which, val) => {
    this.setState(prevState => ({
      fieldObj: {
        ...prevState.fieldObj,
        [which]: val,
      },
    }));
  };

  render() {
    return (
      <div>
        <h2>Parent</h2>
        Value in Parent Component State: {this.state.fieldObj.field1 ? 1 : 0} : {this.state.fieldObj.field2 ? 1 : 0}
        <br />
        <Child obj={this.state.fieldObj} onUpdate={this.updObj} />
        <br />
        <Child obj={this.state.fieldObj} onUpdate={this.updObj} />
        <br />
        <Child obj={this.state.fieldObj} onUpdate={this.updObj} />
      </div>
    )
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);

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

  update = (which) => {
    this.props.onUpdate(which, !this.state.obj[which]);
    this.setState(prevState => ({
      obj: {
        ...prevState.obj,
        [which]: !prevState.obj[which],
      },
    }));
  };

  render() {
    return (
      <div>
        <h4>Child</h4>
        Value in Child State: {this.state.obj.field1 ? 1 : 0} : {this.state.obj.field2 ? 1 : 0}<br />
        <button type="button" onClick={(e) => { this.update('field1') }}>field1</button>
        <button type="button" onClick={(e) => { this.update('field2') }}>field2</button>
      </div>
    )
  }
}

render(<Parent />, document.getElementById('root'));

Upvotes: 1

Views: 62

Answers (2)

Shubham Khatri
Shubham Khatri

Reputation: 281626

When all child components values are directly derivable from the props you do not need to create a state in child which is a replica of props and maintain it, what you need to do is modify the parent's state directly like

import React from 'react';
import { render } from 'react-dom';
import Hello from './Hello';

class Parent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      filedStr: 'some text',
      fieldObj: {
        field1: true,
        field2: true
      }
    }
  }

  updObj = (which, val) => {
    this.setState(prevState => ({
      fieldObj: {
        ...prevState.fieldObj,
        [which]: val,
      },
    }));
  };

  render() {
    return (
      <div>
        <h2>Parent</h2>
        Value in Parent Component State: {this.state.fieldObj.field1 ? 1 : 0} : {this.state.fieldObj.field2 ? 1 : 0}
        <br />
        <Child obj={this.state.fieldObj} onUpdate={this.updObj} />
        <br />
        <Child obj={this.state.fieldObj} onUpdate={this.updObj} />
        <br />
        <Child obj={this.state.fieldObj} onUpdate={this.updObj} />
      </div>
    )
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);

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

  update = (which) => {
    this.props.onUpdate(which, !this.props.obj[which]);
  };

  render() {
    return (
      <div>
        <h4>Child</h4>
        Value in Child State: {this.props.obj.field1 ? 1 : 0} : {this.props.obj.field2 ? 1 : 0}<br />
        <button type="button" onClick={(e) => { this.update('field1') }}>field1</button>
        <button type="button" onClick={(e) => { this.update('field2') }}>field2</button>
      </div>
    )
  }
}

render(<Parent />, document.getElementById('root'));

CodeSandbox

However if you want to why your way of handling doesn't work as expected it, is because, you are not updating the state of the child components based on the state update in the parent, you were only setting it once in the constructor which is only called once when the component mounts, what you need is to implement the componentWillReceiveProps lifecycle function

Upvotes: 1

nitte93
nitte93

Reputation: 1840

Here, I've updated your code to meet your need -https://codesandbox.io/s/llnzm2y95z

Your assumption of child re-rendering is wrong. When the child rerenders the constructor method is not called in other words, constructor is only called once. To use next props and change in states you need to make use of the renders and componentWillReceiveProps. See react-component lifecycle http://busypeoples.github.io/post/react-component-lifecycle/

The problem is when you updated the parent's state using onClick={(e) => { this.update('field1') }} and onClick={(e) => { this.update('field1') }}

You updated the parent's state and this state was again passed to the child. But in the child you are not using this new props. You' re instead using the state, this state was updated only in the constructor, which is not updated after the new props got received. (As the constructor gets called only once)

One way to handle the new props is directly using the props in the render as the component will rerender and the updated props will be available to it.

The other way if you want to make use of the state, then update the state inside componentWillReceiveProps. (I would also want to point out that it is highly not-recommended to do setState inside a componentWillReceiveProps and componentDidMount). So better use the first step.

componentWillReceiveProps(newProps) {
 if(newProps !== this.props){
  this.setState({newStateObjects})
 }
} 

Upvotes: 0

Related Questions