Reputation: 13
I am building a simple recipes list app and am running into a problem. I am probably misunderstanding something fundamental about how React renders.
Here's some of the code from my codepen (scroll down to where I explain my code and my problem)
save(action, item){ //this is the save method of my App component
if (action == 'edit'){ // EDIT
...
} else if (action == 'delete'){ // DELETE
let newRecipes = this.state.recipes.filter( function(recipe){ return recipe.key != item.key } ) //this correctly removes the item I want to delete
this.state.recipes = newRecipes; //making extra sure the state is updated
this.setState({ recipes: newRecipes }); //shouldn't this make App render?
this.forceUpdate(); //cmon App, please re-render
console.log('newRecipes', newRecipes);
console.log('main state', this.state); //the state was updated as expected, but App does not show what the state contains
}
Later, in App's render()
<ul className="well list-group">
{
this.state.recipes.map( (recipe)=>{ //i expect the recipe items generated to reflect App's new state, but they dont :(
return(
<Recipe
save={this.save}
original={recipe}
id={recipe.key}
title={recipe.title}
ingredients={recipe.ingredients}
instructions={recipe.instructions}
/>
)
} )
}
</ul>
My app is like this:
I have an App component at the top that renders all the other components and whose state should contain all the data about the App overall.
state.recipes
is an array with recipe objects. These are passed as properties to the Recipe component, in the App render method. (<Recipe title=... />
)
The <Recipe />
component has methods and buttons to edit and delete the recipe. when a recipe is edited or deleted, the change is handled by the save() method in App, which is also passed to <Recipe />
as a prop.
From my understanding, that is all standard React usage.
This all seems to work as expected. When I edit or delete a recipe the change is properly reflected in the App state.
The Problem
The unexpected behavior happens when deleting a recipe. Even though the correct recipe is removed from App state, that change is not reflected in the rendered HTML. Instead, whatever the last recipe in the App.state.recipes
array is removed and I am left staring at a console log of the state and rendered HTML that show different things.
What I've tried:
I expected that using setState({ recipes: newRecipes})
to update App state to the newly updated recipes list and show it on the screen. But this did not seem to work. I even used .forceUpdate()
but that did not work.
I added some console logging to Recipe's constructor function and learned that it only runs once in the beginning. It doesn't run again as I would expect when the recipes array in App's state is changed.
Upvotes: 1
Views: 156
Reputation: 1681
You missed to put a key property on Recipe elements, because of that React does not know which items are changing and how to react to that change. If you just put the key property everything will work as expected.
<Recipe
save={this.save}
original={recipe}
id={recipe.key}
key={recipe.key} // <-- HERE
title={recipe.title}
ingredients={recipe.ingredients}
instructions={recipe.instructions}
/>
Remember to add a key property to all the components created from iterations.
See your example working here http://codepen.io/damianfabian/pen/egeevR
Upvotes: 1