You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 13:03:11