简体   繁体   中英

React 16.0.0 Parent Child GrandChild. Updating state in Parent does not update GrandChild

Given the following component structure I wan't to update my state in Parent and this should then update my GrandChild. I know I can make it work making Child stateless or only update State in Child but in our application this is the current structure. Is there any good way to do this or should we re-design? We keep a state in parent since we do not wan't to issue to many http requests in the application. In parent we fetch data that we need and make initial modifications. This data in then sent down to other components that in turn have child components, hence the example structure.

What gets printed in this example: Test: 1235

import * as React from 'react';
import * as ReactDOM from 'react-dom';

interface IProps {
}

interface IState {
  cases: number[];
}

class Parent extends React.Component<IProps, IState> {
  constructor(props: IProps) {
    super(props);
    this.state = {
      cases: [1, 2, 3],
    };
  }

  componentDidMount() {
    let newCase = 4;
    let newCases = Array.from(this.state.cases);
    newCases.push(newCase)
    this.setState({
      cases: newCases
    });
  }

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

interface IChildProps {
  cases: number[];
}

interface IChildState {
  cases: number[];
}

class Child extends React.Component<IChildProps, IChildState> {
  constructor(props: IChildProps) {
    super(props);
    this.state = {
      cases: this.props.cases,
    };
  }

  componentDidMount() {
    let newCase = 5;
    let newCases = Array.from(this.state.cases);
    newCases.push(newCase)
    this.setState({
      cases: newCases
    });
  }

  render() {
    return (
      <GrandChild cases={this.state.cases} />
    );
  }
}

interface IGrandChildProps {
  cases: number[];
}

interface IGrandChildState {
}

class GrandChild extends React.Component<IGrandChildProps, IGrandChildState> {
  constructor(props: IGrandChildProps) {
    super(props);
  }

  render() {
    return (
      <div>
        Test: {this.props.cases.map((value, index) => {
          return <span key={index}>{value}</span>
        }
        )}
      </div>
    );
  }
}

export default Parent

The problem here is that you are mapping props to state, once that happens you are the one who is in charge of updating the state when props change. Since you only use componentDidMount it only maps the props to state once. I typically lean towards avoiding having a component convert props into its own state and instead just have the parent pass down the props to the child in whatever way it needs it and not have to worry about changing the state when props change in the child component.

The other option is to use the componentWillReceiveProps lifecycle method and do the following

 setCases(cases) {
    let newCase = 5;
    let newCases = Array.from(cases);
    newCases.push(newCase)
    this.setState({
      cases: newCases
    });
  }

  componentDidMount() {
    this.setCases(this.props.cases)
  }

  componentWillReceiveProps(nextProps) {
    this.setCases(nextProps.cases);
  }

the componentDidMount will handle setting the state on initial loading and then componentWillReceiveProps will handle changing the state whenever the props change.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM