1
votes

A parent component contains a button in an appbar and a drawing area (where I drag and drop some content, that's only for the context here). I want to keep child components relatively independant.

I want to change the state of the child component when clicking on a button available in this parent component. It works when I use "componentWillReceiveProps" which is unsafe (and the replacement function "is to be used rarely". So I never successfully called a function (in the child) from the parent component.

What the best practice in React? (I come from PHP/GO/Python background).

Parent class:

Class EditionView extends React.Component {

  constructor(props) {
    super(props)
    var drawingModel = this.emptyDrawingModel();
    this.state= {drawingModel:drawingModel};
  }

  emptyDrawingModel(){
    var drawingModel = []
    for (var i=0; i<3; i++) {
      drawingModel.push('')
    }
    return drawingModel;
  }

  resetDrawingArea() {
    console.log("tmp:", this);
    var drawingModel = this.emptyDrawingModel();
    this.setState({drawingModel: drawingModel});
  }

  render() {
    console.log(this.state);
    return (
      <div>
        <AppBar position="static" color="primary">
          <Typography variant="h6" color="inherit">
              Title
          </Typography>
          <Toolbar>
            <Button variant='contained' component='span' onClick={()=>{this.resetDrawingArea()}}>Reset</Button>
          </Toolbar>
        </AppBar>
        <Grid container>
          <Grid item sm={9}>
            <DrawingArea id='drawing' initialModel={this.state.drawingModel}/>
          </Grid>
        </Grid>
      </div>
    );
  }

}

export default EditionView;

Child class

class DrawingArea extends React.Component {

  constructor(props) {
    super(props)
    this.state = {content:props.initialModel};
    //this.reset = this.reset.bind(this); 
    //this.reset = this.reset();
  }

  /*
  //WORKs but UNSAFE
  componentWillReceiveProps(){
    console.log("componentWillMount", this)
    this.setState({content: this.props.initialModel});
  }
  */

  reset(){
    console.log('in Drawing Area > reset')
    var model = this.props.initialModel;
    this.setState({content:model})
  }

  render() {
    console.log("render drawing area", this)
    var items = this.state.content.map((val, i) =>{
      var item;
      if (val === ''){
        // code for if 
      } else {
        // code for else
      }
      return item;
    });

    return(
      <div id="main-area">
        <Grid container>{items}</Grid>
      </div>
    )
  }
}

export default DrawingArea;
3

3 Answers

2
votes

Use getDerivedStateFromProps:

getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.

class DrawingArea extends React.Component {

    static getDerivedStateFromProps(props) {
         return {
            content: props.initialModel
         }
    }

  ...
}
1
votes

In your child component, DrawingArea, since you are using this.state in the render function then you probably will need to implement the lifecycle event getderivedstatefromprops.

Another option would be to use this.props in the render function. This should work for the simple example you have provided. It will continue to work for you until the point in time (if ever) you need to store state within DrawingArea.

0
votes

You dont need componentWillReceiveProps() as long as you have in the constructor of the child

constructor(props) {
  super(props)
    this.state = {
      content: this.props.initialModel
    }
}

and as long as you keep using this.state.content in your child component

When this.state.drawingModel is gonna change the parent component is going to rerender making the child component receive its new props and as long as its mapped to the state of the child component this one will rerender also