简体   繁体   中英

How to change this.props in child when parent state changes in React JS?

new to React JS here. I have a setup where I an App parent component that has a initialFormInput state holding a recipe object. A function is passed from the parent component to a child component called EditRecipeButton that can change this state to a specific recipe that is called from an edit button.

This state in the parent is mapped to a state in a child AddRecipe component with a via this.props and my reasoning is that whenever the parent component state changes this state in AddRecipe changes as well. But that doesn't happen, what am I doing wrong here?

Here is my code:

var App = React.createClass({


  getInitialState(){

    return{

      showModal:false,


      recipeKeys: [ ],

      recipes: [ ],

      initialFormInput: {name: "", ingredients: []}

    }


  },


  open: function(){


    this.setState({showModal:true});

  },

    close: function(){

    this.setState({showModal:false});

  },

  editRecipe: function(recipe){

    console.log(recipe);

    this.setState({initialFormInput: recipe}, function(){

      this.open();  


    });


  },

  render: function(){

    return( 

      <div className="container">
        <h1>Recipe Box</h1>
        <RecipeList recipes = {this.state.recipes} deleteRecipe = {this.deleteRecipe} editRecipe={this.editRecipe} />
        <AddRecipeButton openModal = {this.open}/>
        <AddRecipe closeModal = {this.close} showModal={this.state.showModal} addRecipeKey = {this.addRecipeKey} initialFormInput = {this.state.initialFormInput}/>
      </div>

    )  

  }


var RecipeList = function (props) {

    return (
        <ul className="list-group">
            {

          props.recipes.map( (item,index) => <RecipeItem recipe={item} deleteRecipe = {props.deleteRecipe} editRecipe={props.editRecipe}/> )

            }
        </ul>  
    );
};

var RecipeItem = React.createClass({

getInitialState: function(){

    return {displayIngredients: false}

},  

toggleRecipe: function() {

  this.setState({displayIngredients: !this.state.displayIngredients})

},

render: function() {

    return(

      <li className="list-group-item" >
      <h4 onClick={this.toggleRecipe}>{this.props.recipe.name}</h4>
      <div style={{display: this.state.displayIngredients ? 'block' : 'none'}}>
      <h5 className="text-center">Ingredients</h5>
      <hr/> 
       <ul className="list-group" >
        {this.props.recipe.ingredients.map((item) => <IngredientItem ingredient={item} />)} 
       </ul> 
       <ButtonToolbar>
         <DeleteRecipeButton deleteRecipe = {this.props.deleteRecipe} recipeName={this.props.recipe.name}/>  
         <EditRecipeButton editRecipe = {this.props.editRecipe} recipe={this.props.recipe}/>  
       </ButtonToolbar>  
        </div>  
      </li>


    )


}

});

var IngredientItem = function(props){

  return (
  <li className="list-group-item">

    <p>{props.ingredient}</p>
  </li>


  )

};


var EditRecipeButton = React.createClass({

  render: function(){

   return (

     <Button bsStyle="default" bsSize="small" onClick={() => this.props.editRecipe(this.props.recipe)}>Edit</Button>  

   ) 

  }


});


var AddRecipe = React.createClass({
//Form in modal to add recipe

  getInitialState(){

    return {

     name: this.props.initialFormInput.name, 

     ingredients: this.props.initialFormInput.ingredients


    };

  },


  getValidationStateName(){

    var length = this.state.name.length;

    if(length > 0) {

      return "success";

    } else {

      return "error";

    }

  },

  getValidationStateIngredients(){

    var length = this.state.ingredients.length;

    if(length > 0){

      return "success";

    } else {

      return "error";

    }


  },

  handleInput: function(key,e){

    var input = e.target.value;


    if(key === "ingredients"){

        input = e.target.value.split(",");  

    }

    var update = {};

    update[key] = input;

    this.setState(update, function(){

      console.log(this.state);

    });


  }, 

 handleSubmit(){

   var recipe = JSON.stringify({name: this.state.name, ingredients: this.state.ingredients});

   localStorage.setItem(this.state.name, recipe);

   var recipeObject= JSON.parse(recipe);

   this.props.addRecipeKey(recipeObject);

   this.props.closeModal();

   this.setState({name: "", ingredients: []});


  },

  render: function(){ 

    return (

      <div>
        <Modal show={this.props.showModal} onHide={this.props.closeModal}>
            <Modal.Header closeButton>
              <Modal.Title>Add a Recipe Here</Modal.Title>
            </Modal.Header>
            <Modal.Body>
              <form>
        <FormGroup controlId="formNameText" validationState = {this.getValidationStateName()}>
          <ControlLabel>Recipe</ControlLabel>
          <FormControl
            type="text"
            placeholder="Give your recipe a name"
            value={this.state.name}
            onInput={this.handleInput.bind(this,'name')}
            />
          <FormControl.Feedback /> 
        </FormGroup>  
          <br/>
        <FormGroup controlId="formIngredientsTextarea" validationState = {this.getValidationStateIngredients()}>
          <ControlLabel>Ingredients</ControlLabel>
          <FormControl
            componentClass="textarea"
            placeholder="Insert your ingredients, separated by a comma"
            value={this.state.ingredients}
            onInput={this.handleInput.bind(this,'ingredients')}
          />  
          <FormControl.Feedback /> 
          <hr/>
        </FormGroup>

        <Button bsStyle="primary" onClick={this.handleSubmit}>Submit</Button>
      </form>  
            </Modal.Body>
        </Modal>
      </div>


      )
  }

});


ReactDOM.render(<App />, document.getElementById('app'));

});

So this does not change when the parent state changes:

getInitialState(){

    return {

     name: this.props.initialFormInput.name, 

     ingredients: this.props.initialFormInput.ingredients


    };

  },  

As the name suggests, getInitialState only provides the initial state of a component. Subsequent update won't trigger that function.

You need to to implement componentWillReceiveProps to update the state in when props change. From the docs :

componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.

Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.

If you want to share state across components use redux isntead. Also maintain a separate file for each component. This link might help you Step by Step Guide To Building React Redux Apps

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