React映射状态对象数组时无法显示完整状态的问题排查
我在使用React开发一款预算追踪应用时遇到了异常问题:应用包含总预算、添加新支出的表单,以及展示支出项和金额的区域,新支出金额会从总预算中扣除。
我的状态定义如下:
const [total, setTotal] = useState(1500); const [expense, setExpense] = useState({ name: "Expense", cost: "0" }); const [allExpenses, setAllExpenses] = useState([]);
total为总预算,expense是表单输入的新支出项,allExpenses是需要展示的支出对象数组。
我的变更和提交方法如下:
const handleSubmit = event =>{ event.preventDefault(); let exp = expense.name; let cost = expense.cost; alert( "New expense " + exp + " costs " + cost ); setTotal((prevTotal) => prevTotal - cost); const newAll = (prevAll) => ([...prevAll, expense]); setAllExpenses(newAll); } const handleChange = event =>{ setExpense((prevExpense) => ({ ...prevExpense, [event.target.name]: event.target.value })) }
在渲染函数中,我使用map方法展示支出项:
{allExpenses.map(({item, cost}) => {return <h1>{item} costs {cost}$</h1>})}
我还添加了一个Printer组件追踪状态变化:
export function Printer (props){ return( <h1>{props.name} costs {props.cost}$</h1> /* Added as a change tracker. State DOES change. Issue is probably within the allExpenses state. */ ); }
问题在于:状态确实发生了变化,添加新支出(如NewExpense,12$)时,Printer组件能正常显示“NewExpense costs 12$”,但map方法仅显示“ costs 12$”,expense.name属性在映射后无法显示。我尝试重写map调用和setAllExpenses方法,均未解决问题,表单在渲染函数内未通过props传递状态,请问我忽略了什么?
核心问题:解构属性名不匹配
你的expense对象中存储支出名称的属性是name,但在map函数里你解构的是item,这就导致item是undefined,所以渲染时只显示了金额部分。把解构的item改成name即可:{allExpenses.map(({name, cost}) => {return <h1>{name} costs {cost}$</h1>})}额外优化点1:添加key属性
React列表渲染要求每个项都有唯一的key属性,避免渲染警告和性能问题,建议修改为:{allExpenses.map((exp, index) => { return <h1 key={index}>{exp.name} costs {exp.cost}$</h1> })} // 若后续支出项有唯一id,优先用id作为key,index仅在无唯一标识时使用额外优化点2:cost的类型转换
当前cost是字符串类型,计算总预算时prevTotal - cost会自动转成数字,但为了避免潜在的类型错误,建议在提交时主动转换为数值类型:setTotal((prevTotal) => prevTotal - Number(cost)); // 也可以在handleChange阶段就转成数字,确保状态里的cost始终是数值类型额外优化点3:简化setAllExpenses的写法
你当前的newAll写法可以直接简化为回调函数传递,无需单独定义变量:setAllExpenses(prevAll => [...prevAll, expense]);
内容的提问来源于stack exchange,提问作者len4o17

