Reputation: 1406
I'm relatively new to React and creating a todo list style Recipe app. The user should be able to add a new recipe, delete an existing recipe, or edit an existing recipe. I am having trouble writing a function that allows the user to click the edit button and then submit a new recipe title and/or new ingredients to that recipe item.
Here is the project. How can I make it so after clicking one of the edit buttons and submitting the form the recipe title/ingredients of that recipe item are updated to show whatever the user submitted? I have a function called onSubmit
in my App.js file that handles new recipe items being created by the user and adds them to the list. I am now trying to implement a similar function called onEditSubmit
that allows the user to update existing recipe items but I am stuck.
Here is my App.js where the state lives:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
items: ["Pumpkin Pie", "Spaghetti", "Onion Pie"],
ingredients:[
["Pumpkin Puree ", "Sweetened Condensed Milk ", "Eggs ", "Pumpkin Pie Spice ", "Pie Crust "],
["Noodles ", "Tomato Sauce ", "(Optional) Meatballs "],
["Onion ", "Pie Crust "]
],
// Recipe name and ingredients
inputVal: '',
ingredientVal: '',
// Recipe name and ingredients when user is editing existing recipe
inputValEdit: '',
ingredientValEdit: '',
// Controls whether forms are displayed or hidden
showRecipeForm: false,
showRecipeEditForm: false
};
}
// Get text user inputs for recipes
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
};
// When user submits recipe this adds it to the list
onSubmit = (event) => {
event.preventDefault()
this.setState({
items: [...this.state.items, this.state.inputVal],
ingredients: [...this.state.ingredients, this.state.ingredientVal],
showRecipeForm: false
});
}
// Should edit and update a recipe item when user clicks edit button and then submits RecipeEditForm, not working
onEditSubmit = (event) => {
event.preventDefault()
// This setState shoul erase previous recipe state and rewrite it with the details the user entered after editing that particular recipe item
this.setState({
items: this.state.inputValEdit,
ingredients: this.state.ingredientValEdit,
showRecipeEditForm: false
});
}
closeRecipeForm = () => {
this.setState({
showRecipeForm: false,
showRecipeEditForm: false
});
}
// Shows recipe
AddRecipe = (bool) => {
this.setState({
showRecipeForm: bool
});
}
// Is called when one of the edit recipe buttons is clicked, shows RecipeEditForm
edit = (item, index) => {
console.log('Edit button clicked');
console.log('index is ' + index);
this.setState({ showRecipeEditForm: !this.state.showRecipeEditForm });
}
// Deletes recipe item from the list
delete = (item, index) => {
this.setState({
ingredients : this.state.ingredients.filter((_, i) => i !== index),
items: this.state.items.filter((_, i) => i !== index)
});
}
render() {
return (
<div className="Recipe-List">
<h1>Recipe List</h1>
<Item
items={this.state.items}
ingredients={this.state.ingredients}
edit={this.edit}
delete={this.delete}
/>
<button onClick={this.AddRecipe}>Add New Recipe</button>
{/* Shows form to edit recipe */}
{ this.state.showRecipeEditForm ?
<RecipeEditForm
inputValEdit={this.state.inputValEdit}
handleChange={this.handleChange}
ingredientValEdit={this.state.ingredientValEdit}
onEditSubmit={this.onEditSubmit}
closeRecipeForm={this.closeRecipeForm}
/>
:null
}
{/* Shows form to add new recipe to the list */}
{ this.state.showRecipeForm ?
<RecipeForm
inputVal={this.state.inputVal}
handleChange={this.handleChange}
ingredientVal={this.state.ingredientVal}
onSubmit={this.onSubmit}
closeRecipeForm={this.closeRecipeForm}
/>
:null
}
</div>
);
}
}
And here is my Item.js function container for the data. This container works fine when onSubmit
utilizes it to add a new recipe. But when onEditSubmit
attempts to use it I get an error, props.items.map is not a function
.
import React from 'react';
const Item = (props) => (
<div>
<div className="Recipe-Item" key={props.text}>
{props.items.map((item, index) =>
<div className="Recipe-Item-Container">
<ul>
<li key={index}>
{item}
</li>
<li>
{props.ingredients[index]}
</li>
</ul>
<button onClick={() => props.edit(item, index)}>Edit</button>
<button onClick={() => props.delete(item, index)}>Delete</button>
</div>
)}
</div>
</div>
)
export default Item;
Pretty sure I'm making a noob React error but I'm not sure what the problem is.
Edited to correct syntax error in onSubmitError function: inputVal changed to inputValEdit, ingredientVal changed to ingredientValEdit
Here are the functional components for onSubmit
:
import React, { Component } from 'react';
const RecipeForm = (props) => {
return (
<div>
<form className="Recipe-Form" onSubmit={props.onSubmit}>
<p className="x-close" onClick={props.closeRecipeForm}>X</p>
<div className="form-content">
<label>Recipe Name</label>
<input
name="inputVal"
value={props.inputVal}
onChange={props.handleChange}
/>
<label>Ingredients</label>
<input
name="ingredientVal"
value={props.ingredientVal}
onChange={props.handleChange}
/>
<button>Submit</button>
</div>
</form>
</div>
);
}
export default RecipeForm;
And here is the functional component for onEditSubmit
:
import React, { Component } from 'react';
const RecipeEditForm = (props) => {
return (
<div>
<form className="Recipe-Form Edit-Form" onSubmit={props.onEditSubmit}>
<p className="x-close" onClick={props.closeRecipeForm}>X</p>
<div className="form-content">
<h1>This is the edit form</h1>
<label>Edit Recipe</label>
<input
name="inputValEdit"
value={props.inputValEdit}
onChange={props.handleChange}
/>
<label>Ingredients</label>
<input
name="ingredientValEdit"
value={props.ingredientValEdit}
onChange={props.handleChange}
/>
<button>Submit</button>
</div>
</form>
</div>
);
}
export default RecipeEditForm;
Upvotes: 1
Views: 359
Reputation: 30360
You need to extend your <App/>
state so that your app can track which recipe item in your list of recipes is currently being edited.
You could for instance, store the index of the recipe data that your user is editing. You would then use this index to update the correct recipe in your list during the onEditSubmit()
function:
onEditSubmit = (event) => {
event.preventDefault()
// Get the index of the recipe we're currently editing
const currentlyEditingIndex = this.state.currentlyEditingIndex
// Clone data arrays for mutation
const items = [].concat(this.state.items)
const ingredients = [].concat(this.state.ingredients)
// Update data arrays
items[ currentlyEditingIndex ] = this.state.inputValEdit;
ingredients[ currentlyEditingIndex ] = this.state.ingredientValEdit;
this.setState({
items: items,
ingredients: ingredients,
showRecipeEditForm: false
});
}
For this to work, you would also need to update your edit
function like so:
edit = (item, index) => {
console.log('Edit button clicked');
console.log('index is ' + index);
this.setState({
// Store the index of the recipe being edited
currentlyEditingIndex : index,
showRecipeEditForm: !this.state.showRecipeEditForm
});
}
Hope this helps!
Upvotes: 0
Reputation: 582
onEditSubmit = (event) => {
event.preventDefault()
// This setState shoul erase previous recipe state and rewrite it with the
details the user entered after editing that particular recipe item
this.setState({
items: this.state.inputVal,
ingredients: this.state.ingredientVal,
showRecipeEditForm: false
});
}
you're setting the items
state field to a string. You should change it to something like:
onEditSubmit = (e) => {
e.preventDefault();
const {items, ingredients, inputVal, ingredientVal, editingIndex} = this.state;
items[editingIndex] = inputVal;
ingredients[editingIndex] = ingredientVal;
this.setState({
items,
ingredients,
inputVal: '',
ingredientVal: ''
});
}
Where editingIndex
you can store in your edit
function: setState({editingIndex: index}
Upvotes: 2