TypeScript React:向对象内嵌套映射数组添加值时遇undefined错误
解决React中点击+block按钮报错
key.boxes is undefined的问题 报错的核心是**+block按钮的逻辑完全错误**:你调用setStress([...key.boxes, false])时,直接把当前元素的boxes数组替换成了整个stress状态。原本stress是包含Stress对象的数组,这么操作后,stress数组里的元素变成了单个布尔值,下一次渲染时遍历stress,每个key变成布尔值,自然没有boxes属性,就会抛出key.boxes is undefined的错误。
另外还有几个需要修正的问题:
useState用了Array<any>类型,失去TypeScript的类型校验优势- SVG方块的点击逻辑
setStress(!box)没有正确更新对应位置的布尔值 - 数组遍历
map时未设置唯一key,会触发React性能警告
修复步骤
1. 修正State类型定义
把useState<Array<any>>改为useState<Stress[]>,让类型校验生效:
const [stress, setStress] = useState<Stress[]>([{ label: "", boxes: [false] }]);
2. 修复+block按钮逻辑
点击+block时,要找到当前元素在stress数组中的索引,只更新该元素的boxes数组,保持其他元素不变:
<button onClick={() => { setStress(prev => prev.map((item, idx) => { if (idx === currentIndex) { return { ...item, boxes: [...item.boxes, false] }; } return item; })); }}>+block</button>
需要在stress.map时拿到当前元素的索引,所以把stress.map((key: Stress) =>改成stress.map((key: Stress, idx: number) =>,将idx传递给点击事件。
3. 修复SVG方块的点击逻辑
点击SVG时切换对应位置的布尔值,同样需要元素索引和box的索引:
<rect className="stress" style={{ fill: box ? "red" : "white" }} height={25} width={25} onClick={() => { setStress(prev => prev.map((item, sIdx) => { if (sIdx === idx) { return { ...item, boxes: item.boxes.map((b, bIdx) => bIdx === boxIdx ? !b : b) }; } return item; })); }} />
在key.boxes.map时拿到box的索引boxIdx。
4. 添加唯一Key
在map遍历的外层div上添加key属性(示例用索引,实际项目建议用唯一ID):
stress.map((key: Stress, idx: number) => ( <div key={idx}> {/* 其他内容 */} </div> ))
完整修复代码
import { useState } from "react"; interface Stress { label: string boxes: boolean[] } const Stress: React.FC = () => { const [stress, setStress] = useState<Stress[]>([{ label: "", boxes: [false] }]); const [edit, isEdit] = useState<boolean>(false); return ( <div className="characterSheetBox"> <h1>STRESS</h1> <button className="characterSheetButton" onClick={() => setStress([...stress, { label: "", boxes: [false] }])}>+blocks</button> <button className="characterSheetButton" onClick={() => isEdit(!edit)}>Edit</button> <div> {edit ? stress.map((key: Stress, idx: number) => ( <div key={idx}> <input type="text" value={key.label} onChange={(e) => { setStress(prev => prev.map((item, sIdx) => { if (sIdx === idx) { return { ...item, label: e.target.value }; } return item; })); }} /> {key.boxes.map((box: boolean, boxIdx: number) => ( <div key={boxIdx}> <svg> <rect className="stress" style={{ fill: box ? "red" : "white" }} height={25} width={25} onClick={() => { setStress(prev => prev.map((item, sIdx) => { if (sIdx === idx) { return { ...item, boxes: item.boxes.map((b, bIdx) => bIdx === boxIdx ? !b : b) }; } return item; })); }} /> </svg> </div> ))} <button onClick={() => { setStress(prev => prev.map((item, sIdx) => { if (sIdx === idx) { return { ...item, boxes: [...item.boxes, false] }; } return item; })); }}>+block</button> </div> )) : stress.map((key, idx) => ( <div key={idx}> <p>{ key.label }</p> {key.boxes.map((box, bIdx) => ( <div key={bIdx}> <svg> <rect className="stress" style={{ fill: box ? "red" : "white" }} height={25} width={25} /> </svg> </div> ))} </div> )) } </div> </div> ) } export default Stress;
内容的提问来源于stack exchange,提问作者bascott
相关产品推荐
相关产品推荐

