Redux Toolkit状态更新问题:如何为指定食材对象添加并更新ingredientMeasurementUpdate键值对
精准更新Redux中指定食材字段的解决方案
我一眼就看到几个关键问题,咱们一步步捋清楚,把这个状态更新的问题解决掉:
1. 核心问题:Reducer把数组当成对象处理了
先看你updateFields的代码:
updateFields: (state, action) => { state.ingrediants = { ...state.ingrediants, ...action.payload }; },
原来的state.ingrediants是数组,你用对象展开语法{...}直接把它转成了带索引键的对象,还把action.payload里的字段直接挂在了这个对象上——这就是Redux DevTools里数组变对象、全局多了个ingredientMeasurementUpdate的原因。
另外提一句:你整个代码里的ingrediants都多写了一个i,正确拼写是ingredients,虽然不影响运行,但这种不一致很容易搞晕自己,后面我会统一修正。
2. 组件层缺失关键信息:没告诉Redux要更新哪个食材
你的RecipeList里所有食材共用同一个ingredientMeasurementUpdate状态,提交时只传了数值,没传递目标食材的ID。这就像你去超市说“我要改价格”却不说改哪个商品,系统肯定不知道该操作谁。
解决方案分步走
第一步:修复Reducer的更新逻辑
我们需要遍历食材数组,精准找到对应ID的食材,只更新它的ingredientMeasurementUpdate字段:
import { createSlice } from '@reduxjs/toolkit'; const initialState = { ingredients: [] }; // 修正拼写 export const ingredientsSlice = createSlice({ name: "ingredientList", initialState, reducers: { addIngredients: (state, action) => { // 新增食材时默认给ingredientMeasurementUpdate赋值0 state.ingredients = [...state.ingredients, { ...action.payload, ingredientMeasurementUpdate: 0 }]; }, removeIngredients: (state, action) => { // 修正拼写 state.ingredients = state.ingredients.filter( (i) => i.id !== action.payload.id ); }, // 把action改名,更清晰表达意图 updateIngredientMeasurement: (state, action) => { const { id, updateValue } = action.payload; state.ingredients = state.ingredients.map(ingredient => { if (ingredient.id === id) { // 只更新目标食材的指定字段,其余保持不变 return { ...ingredient, ingredientMeasurementUpdate: Number(updateValue) // 转成数字,和初始类型一致 }; } return ingredient; }); }, }, }); // 修正导出的action名 export const { addIngredients, removeIngredients, updateIngredientMeasurement } = ingredientsSlice.actions; export default ingredientsSlice.reducer;
第二步:修改RecipeList组件,传递目标食材ID
每个食材的表单需要关联自己的ID,提交时把ID和更新值一起传给Redux:
import { memo, useSelector, useDispatch } from 'react-redux'; import { updateIngredientMeasurement } from './ingredientsSlice'; export const RecipeList = memo(() => { const ingredientList = useSelector( (state) => state.ingredientList.ingredients // 修正拼写 ); const dispatch = useDispatch(); // 每个表单提交时带上当前食材的ID const handleSubmit = (e, ingredientId) => { e.preventDefault(); const inputValue = e.target.elements.updateInput.value; dispatch( updateIngredientMeasurement({ id: ingredientId, updateValue: inputValue }) ); // 清空输入框 e.target.elements.updateInput.value = ""; }; return ( <div> <div> {ingredientList.map((ingredient) => ( <div className="border-gray-800 border h-52 w-full p-4" key={ingredient.id} > <p className="mb-2">{`${ingredient.ingredientMeasurement}${ingredient.ingredientUnit} ${ingredient.ingredientName}`}</p> <p className="mb-2">当前更新值: {ingredient.ingredientMeasurementUpdate}</p> <form onSubmit={(e) => handleSubmit(e, ingredient.id)}> <fieldset className="mb-2"> <input className="border-2 p-1" type="text" name="updateInput" placeholder="输入更新数值" /> </fieldset> <fieldset> <button type="submit" className="border-2 p-1">更新</button> </fieldset> </form> </div> ))} </div> </div> ); });
第三步:修正其他拼写细节(可选但推荐)
比如IngrediantsForm组件里的变量名和ID,统一拼写避免后续踩坑:
import { memo, useState, useCallback, useDispatch } from 'react'; import { addIngredients } from './ingredientsSlice'; import { v4 as uuidV4 } from 'uuid'; export const IngredientsForm = memo(() => { // 修正组件名 const dispatch = useDispatch(); const [ingredientName, setIngredientName] = useState(""); // 修正拼写 const [ingredientMeasurement, setIngredientMeasurement] = useState(""); // 修正拼写 const [ingredientUnit, setIngredientUnit] = useState(""); // 修正拼写 const onFormSubmit = useCallback( (e) => { e.preventDefault(); dispatch( addIngredients({ ingredientName, ingredientMeasurement: Number(ingredientMeasurement), // 转成数字 ingredientUnit, id: uuidV4(), }) ); // 清空表单 setIngredientName(""); setIngredientMeasurement(""); setIngredientUnit(""); }, [ingredientName, ingredientMeasurement, ingredientUnit, dispatch] ); return ( <form onSubmit={onFormSubmit} className="space-y-2 p-4 border-2 w-80"> <fieldset> <input className="border-2 p-1 w-full" type="text" value={ingredientName} id="ingredientname" // 修正拼写 onChange={(e) => setIngredientName(e.target.value)} placeholder="食材名称" /> </fieldset> <fieldset> <input className="border-2 p-1 w-full" type="number" value={ingredientMeasurement} id="ingredientmeasurement" // 修正拼写 onChange={(e) => setIngredientMeasurement(e.target.value)} placeholder="数量" /> </fieldset> <fieldset> <input className="border-2 p-1 w-full" type="text" value={ingredientUnit} id="ingredientunit" // 修正拼写 onChange={(e) => setIngredientUnit(e.target.value)} placeholder="单位" /> </fieldset> <fieldset> <button className="border-2 p-1 w-full bg-gray-100" type="submit"> 添加食材 </button> </fieldset> </form> ); });
为什么这样改就对了?
- Reducer层:用
map遍历数组,只修改匹配ID的食材项,保持数组结构不变,精准更新目标字段; - 组件层:每个食材表单提交时都带上自己的ID,Redux能明确知道要操作哪个对象;
- 类型统一:把输入框的字符串值转成数字,和初始状态的类型保持一致,避免类型不一致的潜在问题;
- 拼写统一:修正了所有
ingrediants的拼写错误,减少后续维护时的混淆。
现在你再打开Redux DevTools观察状态,数组会保持原结构,ingredientMeasurementUpdate也会正确出现在对应的食材对象里啦!
内容的提问来源于stack exchange,提问作者FamousLastWords
相关产品推荐
相关产品推荐

