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 comparethis.props
andnextProps
and perform state transitions usingthis.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.