React调用setState时抛出Cannot read properties of null (reading 'toString')错误
核心问题定位
错误Cannot read properties of null (reading 'toString')源于以下几个代码逻辑漏洞:
1. 错误的对象/数组拷贝逻辑
const tempBusPoints = route.direction !== "IDA E VOLTA" ? { ...busPoints } : [...busPoints];
当route.direction为"IDA E VOLTA"时,代码尝试用数组展开语法拷贝对象类型的busPoints,会创建错误的数组结构(而非预期的对象)。后续访问tempBusPoints.returnBusPoints会得到undefined,引发数据处理异常,最终在渲染或API调用时触发null的toString调用。
2. 未处理找不到目标busPoint的情况
const index = tempAdjacenteBusPoints.indexOf(tempAdjacenteBusPoints.find(bp => bp.id === id)); const draggedBusPoint = tempAdjacenteBusPoints[index];
如果find方法找不到匹配id的busPoint,会返回null,此时indexOf(null)返回-1,draggedBusPoint变成undefined。直接修改undefined的属性会破坏数据结构,引发后续渲染错误。
3. Math.max处理空数组导致异常值
当所有过滤后的订单数组为空时,Math.max(...[])返回-Infinity,赋值给draggedBusPoint.order后,后续逻辑可能将这个异常值传入需要转字符串的场景,间接触发错误。
4. 直接修改原state引用
tempAdjacenteBusPoints是原state数组的浅拷贝,draggedBusPoint是数组元素的引用,直接修改其属性违反React的immutable原则,引发不可预测的组件重渲染副作用。
具体修复步骤
1. 修正busPoints拷贝逻辑
无论route.direction是什么,都保持对象拷贝(若需深拷贝内部数组,需额外处理):
// 浅拷贝busPoints对象,同时深拷贝内部数组 const tempBusPoints = { ...busPoints, goBusPoints: [...busPoints.goBusPoints], returnBusPoints: [...busPoints.returnBusPoints] };
2. 增加busPoint存在性检查
在获取拖拽的busPoint前,先确认找到目标元素:
const foundBusPoint = tempAdjacenteBusPoints.find(bp => bp.id === id); if (!foundBusPoint) { console.warn(`未找到ID为${id}的站点`); return; } const index = tempAdjacenteBusPoints.indexOf(foundBusPoint); // 创建busPoint副本,避免修改原引用 const draggedBusPoint = { ...foundBusPoint };
3. 处理Math.max空数组场景
给Math.max设置默认值,避免出现-Infinity:
// 以goMaxOrder为例 const goOrders = [ ...tempBusPoints.goBusPoints.filter(bp => bp.order !== null).map(bp => bp.order), ...tempAdjacenteBusPoints.filter(bp => bp.state === "added-go" && bp.order !== null).map(bp => bp.order) ]; const goMaxOrder = (goOrders.length > 0 ? Math.max(...goOrders) : 0) + 1;
同理修改returnMaxOrder和setOrder中的Math.max逻辑。
4. 更新数组元素时使用副本
修改draggedBusPoint后,替换数组中的原元素:
draggedBusPoint.state = currentDirection === "go" ? "added-go" : "added-return"; draggedBusPoint.color = "green"; draggedBusPoint.lat = lat; draggedBusPoint.lng = lng; // 替换数组中的原元素 tempAdjacenteBusPoints[index] = draggedBusPoint;
5. 调试辅助(可选)
在setState调用处添加try-catch,定位具体错误点:
try { setSelectedBusPoint(draggedBusPoint); setAdjacenteBusPoints(tempAdjacenteBusPoints); } catch (err) { console.error("状态更新错误:", err, "当前站点数据:", draggedBusPoint); }
内容的提问来源于stack exchange,提问作者JHOSEF NASCIMENTO

