I have a form that I use to create a new recipe with. This form contains a list of ingredients a user can add to and delete from. It looks like this:
I have an Ingredient Component which is just the quantity, unit, and title input fields. What I'm struggling with is how to keep track of the list of ingredients, each with its own quantity, unit, and title states.
Hierarchy is
Recipe Form Component
-> Individual Ingredient components
When I click the plus button, it appends an Ingredient Component to the list of ingredients state which is just a list of Ingredient components. I dont think that's the right approach because I don't see how I can extract each ingredient's state (quantity, unit, title) when I need to save.
So:
Thanks in advance.
To store the collective state of the ingredients in the Recipe Form Component, you should use an array of objects. Each object represents an ingredient with its own quantity, unit, and title. When you click the plus button, you can add an empty ingredient object to the list. Then, you can render the ingredient components by mapping over the array of ingredients.
To modify the list of ingredients based on the new ingredient information in each individual component, you can pass the entire list of ingredients to each ingredient component and edit the list based on the index of the ingredient being modified or deleted. This approach allows you to update the state in the Recipe Form Component, which holds the collective state of all the ingredients.
By using this approach, each ingredient component can have its own state for quantity, unit, and title, while the Recipe Form Component maintains the collective state of the ingredients. here is the code, give it try:
import React, { useState } from "react";
const RecipeForm = () => {
const [ingredients, setIngredients] = useState([
{ quantity: "", unit: "", title: "" },
]);
const handleAddIngredient = () => {
setIngredients((prevIngredients) => [
...prevIngredients,
{ quantity: "", unit: "", title: "" },
]);
};
const handleIngredientChange = (index, updatedIngredient) => {
setIngredients((prevIngredients) => {
const newIngredients = [...prevIngredients];
newIngredients[index] = updatedIngredient;
return newIngredients;
});
};
const handleDeleteIngredient = (index) => {
setIngredients((prevIngredients) => {
const newIngredients = [...prevIngredients];
newIngredients.splice(index, 1);
return newIngredients;
});
};
return (
<div>
{ingredients.map((ingredient, index) => (
<Ingredient
key={index}
ingredient={ingredient}
onChange={(updatedIngredient) =>
handleIngredientChange(index, updatedIngredient)
}
onDelete={() => handleDeleteIngredient(index)}
/>
))}
<button onClick={handleAddIngredient}>Add Ingredient</button>
</div>
);
};
const Ingredient = ({ ingredient, onChange, onDelete }) => {
const { quantity, unit, title } = ingredient;
const handleInputChange = (event) => {
const { name, value } = event.target;
onChange({ ...ingredient, [name]: value });
};
return (
<div>
<input
type="text"
name="quantity"
value={quantity}
onChange={handleInputChange}
/>
<input
type="text"
name="unit"
value={unit}
onChange={handleInputChange}
/>
<input
type="text"
name="title"
value={title}
onChange={handleInputChange}
/>
<button onClick={onDelete}>Delete</button>
</div>
);
};
export default RecipeForm;
By using this approach you are not passing the whole state to each ingredient instead you pass only the onChange method to Ingredient, which only changes that object in the state.