React.js中numOfI状态无修改时两次console.log结果大幅差异问题
问题根源
1. 核心反模式:不要在State中存储组件实例
React的state设计用来存储纯数据,而非渲染好的组件实例。你当前把<IngredientInput />实例存在numOfI state中,直接导致了闭包陷阱:
你每次调用handleAdditionalIngredient生成新组件时,绑定的handleRemoveIngredient是当时渲染闭包中的函数,函数内部捕获的numOfI是组件创建时刻的旧状态值,而非最新状态。你点击删除按钮时触发的是旧闭包里的函数,打印的自然是旧的numOfI值,和当前最新状态不一致,就会出现「状态自行更新」的错觉。
2. 浏览器控制台的引用类型打印坑
你打印的数组是引用类型,浏览器控制台不会在打印时刻快照保存对象内容,只会在你手动展开数组时才读取当前内存中的值,进一步混淆了你的判断。如果要查看打印时刻的真实值,改成console.log(JSON.parse(JSON.stringify(numOfI)))即可验证。
3. 表单状态处理逻辑错误
你当前的handleChange写法无法正确更新formState里的ingredients数组:直接用[event.target.name]作为key赋值,不会修改数组中对应索引的对象属性,后续提交表单也拿不到正确的配料数据。
修复方案
直接复用你已经存在的formState.ingredients数组控制渲染,完全可以删掉多余的numOfI state:
import { useState } from 'react'; import { createRecipe } from '../services/recipeService'; import IngredientInput from '../componenets/IngredientInput'; export default function RecipeNew(props) { const [formState, setFormState] = useState({ creator: '', access: '', name: '', ingredients: [{ amount: '', ingred: '' }], steps: [] }); async function handleSubmit(event) { event.preventDefault(); createRecipe(formState); setFormState({ creator: '', access: '', name: '', ingredients: [{ amount: '', ingred: '' }], steps: [] }); props.history.push('/home'); } // 配料项变更处理 function handleIngredientChange(index, field, value) { const newIngredients = [...formState.ingredients]; newIngredients[index][field] = value; setFormState({...formState, ingredients: newIngredients}); } // 新增配料 function handleAdditionalIngredient() { setFormState({ ...formState, ingredients: [...formState.ingredients, { amount: '', ingred: '' }] }); } // 删除配料 function handleRemoveIngredient(index) { const newIngredients = formState.ingredients.filter((_, i) => i !== index); setFormState({...formState, ingredients: newIngredients}); } return ( <div> <form onSubmit={handleSubmit}> <div className='ingred'> <input type='text' name='name' placeholder='Name' onChange={e => setFormState({...formState, name: e.target.value})} /> {/* 直接根据ingredients数组渲染组件,不需要额外state存组件 */} {formState.ingredients.map((item, index) => ( <IngredientInput key={index} first={index === 0} amount={item.amount} ingred={item.ingred} onChange={(field, value) => handleIngredientChange(index, field, value)} onRemove={() => handleRemoveIngredient(index)} /> ))} <button type="button" onClick={handleAdditionalIngredient}>+</button> </div> <div className='steps'> <textarea cols="40" rows="5" name='steps[]' placeholder={`Step 1`} /> </div> <button>Submit</button> </form> </div> ); }
对应修改IngredientInput组件:
export default function IngredientInput(props){ return( <div> <input type='text' value={props.amount} onChange={e => props.onChange('amount', e.target.value)} placeholder='#' /> <input type='text' value={props.ingred} onChange={e => props.onChange('ingred', e.target.value)} placeholder='Ingredient' /> {!props.first && <button onClick={props.onRemove} type='button'>-</button>} </div> ); }
内容的提问来源于stack exchange,提问作者Keyframe
相关产品推荐
相关产品推荐

