I am building a React application that uses an input field to allow users to type a recipe title, then, when they submit the form, the recipe should be added to a RecipeList.
At the moment, my code isn’t working correctly because I can only type 1 word before the input field loses focus. If I had to guess, I would guess that the form is re-rendering somehow when I type a letter.
I want to continue typing without my input losing focus.
Here is my code:
function RecipeApp() {
const [recipes, setRecipes] = useState(initialRecipes);
const [recipeInput, setRecipeInput] = useState("");
const [summaryInput, setSummaryInput] = useState("");
const [ingredientsInput, setIngredientsInput] = useState([]);
const [cookTimeInput, setCookTimeInput] = useState("");
function handleAddRecipe(e) {
e.preventDefault();
if (!recipeInput || !summaryInput || !ingredientsInput || !cookTimeInput) {
alert("Please fill out all fields!");
return;
}
const newRecipe = {
id: recipes.length,
title: recipeInput,
summary: summaryInput,
ingredients: ingredientsInput.split(",").reduce(
(acc, ing, idx) => ({
...acc,
[`ingredient${idx + 1}`]: ing.trim(),
}),
{}
),
cookTime: parseInt(cookTimeInput, 10),
};
setRecipes([...recipes, newRecipe]);
setRecipeInput("");
setSummaryInput("");
setIngredientsInput("");
setCookTimeInput("");
}
function InputForm() {
return (
<form onSubmit={handleAddRecipe}>
<p>Recipe name</p>
<input
value={recipeInput}
onChange={(e) => setRecipeInput(e.target.value)}
/>
<p>Summary</p>
<input value={summaryInput} placeholder="Enter a description" />
<p>Ingredients</p>
<input
value={ingredientsInput}
placeholder="List up to four ingredients, seperated by a comma"
/>
<p>Cook Time (minutes)</p>
<input value={cookTimeInput} placeholder="Time in minutes" />
<button>Add</button>
</form>
);
}
function RecipeList() {
return (
<ul>
{recipes.map((recipe) => (
<li key={recipe.id}>
{recipe.title}
<button>View</button>
<button>Delete</button>
</li>
))}
</ul>
);
}
return (
<div className="RecipeApp">
<InputForm />
<h2>List of recipes:</h2>
<RecipeList />
</div>
);
}
CAUSE:
InputForm has input which are calls a setState function to update the value. useState causes re-render of the whole functional component. This is causing your code to re-render complete ReceipeApp where it returns JSX with InputForm and ReceipeList. So, here InputForm is re-rendered with existing values of (recipe, cook time, receipe list etc..) on every letter typed in any of the input.
SOLUTION:
InputForm function must be outside the ReceipeApp function. Try to have every function separately and pass props if required.
Example: InputForm may look like this
function InputForm() {
const [recipes, setRecipes] = useState([]);
const [recipeInput, setRecipeInput] = useState("");
const [summaryInput, setSummaryInput] = useState("");
const [ingredientsInput, setIngredientsInput] = useState([]);
const [cookTimeInput, setCookTimeInput] = useState("");
function handleAddRecipe(e) {
e.preventDefault();
if (!recipeInput || !summaryInput || !ingredientsInput || !cookTimeInput) {
alert("Please fill out all fields!");
return;
}
const newRecipe = {
id: recipes.length,
title: recipeInput,
summary: summaryInput,
ingredients: ingredientsInput.split(",").reduce(
(acc, ing, idx) => ({
...acc,
[`ingredient${idx + 1}`]: ing.trim(),
}),
{}
),
cookTime: parseInt(cookTimeInput, 10),
};
setRecipes([...recipes, newRecipe]);
setRecipeInput("");
setSummaryInput("");
setIngredientsInput("");
setCookTimeInput("");
}
return (
<form onSubmit={handleAddRecipe}>
<p>Recipe name</p>
<input
value={recipeInput}
onChange={(e) => {setRecipeInput(e.target.value)}}
/>
<p>Summary</p>
<input value={summaryInput} placeholder="Enter a description"
onChange={(e) => {setSummaryInput(e.target.value)}}/>
<p>Ingredients</p>
<input
value={ingredientsInput}
placeholder="List up to four ingredients, seperated by a comma"
onChange={(e) => {setIngredientsInput(e.target.value)}}
/>
<p>Cook Time (minutes)</p>
<input value={cookTimeInput} placeholder="Time in minutes"
onChange={(e) => {setCookTimeInput(e.target.value)}}/>
<button type='submit'>Add</button>
</form>
);
}
Note: Add type=”submit” to your form button and handle onchange in all input fields
Ajay Kumar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.