未使用useEffect却触发Maximum update depth exceeded警告求助
解决Maximum update depth exceeded警告(仅开发者工具打开时触发)
问题描述
触发Maximum update depth exceeded警告,且仅在浏览器开发者工具打开时出现,代码中未使用会引发该问题的useEffect。
问题根源
- useSelector比较逻辑缺陷:使用引用相等(
===)比较对象类型的stageAttrs,而Redux状态更新时即使对象内容未变,引用变化也会导致useSelector判定状态改变,触发组件不必要重渲染。当开发者工具打开时,Redux DevTools的状态监听会放大这个问题,结合onMouseMove的持续dispatch,形成更新循环。 - Redux reducer违反immutable原则:
mouseMovereducer中直接修改原state对象(如state.images[state.selectedImage].regions = ...),这种突变会导致状态更新异常,在devtools监听时更容易引发不可预测的渲染循环。
解决方案
1. 修复useSelector的比较逻辑
将对象的引用比较改为内容比较,可使用深度比较函数(如lodash的isEqual)或手动解构比较:
import { isEqual } from 'lodash'; import { useDispatch, useSelector } from "react-redux"; const ImageCanvas = () => { const dispatch = useDispatch(); const state = useSelector( (state) => state.general, (a, b) => a.selectedTool === b.selectedTool && a.selectedImage === b.selectedImage && a.contextMenuOptions.region === b.contextMenuOptions.region && isEqual(a.stageAttrs, b.stageAttrs) // 深度比较stageAttrs内容 ); // ... };
2. 修正Redux reducer的immutable更新
所有状态更新必须返回新对象,禁止直接修改原state:
// 修改后的mouseMove reducer mouseMove: (state, action) => { const { x, y } = action.payload; if (!state.mode) return state; const activeImage = state.images[state.selectedImage]; const region = activeImage?.regions?.find(r => r.id === state.mode.regionID); if (!region) return state; switch (state.mode.mode) { case "DRAW_BOX": { return { ...state, images: { ...state.images, [state.selectedImage]: { ...activeImage, regions: activeImage.regions.map(r => r.id === region.id ? { ...r, w: x - region.x, h: y - region.y } : r ) } } }; } case "DRAW_POLYGON": { return { ...state, images: { ...state.images, [state.selectedImage]: { ...activeImage, regions: activeImage.regions.map(r => r.id === region.id ? { ...r, points: [...r.points.slice(0, -1), [x, y]] } : r ) } } }; } default: return state; } };
3. 优化鼠标移动的dispatch频率
添加位置判断,仅当鼠标位置变化时才dispatch动作,减少不必要的状态更新:
const ImageCanvas = () => { // ... const prevMousePos = useRef({ x: 0, y: 0 }); const handleMouse = (op, e) => { e.evt.preventDefault(); const { x, y } = getMousePos(e); switch (op) { // ...其他case case "move": // 仅位置变化时才dispatch if (x !== prevMousePos.current.x || y !== prevMousePos.current.y) { prevMousePos.current = { x, y }; dispatch(mouseMove({ x, y })); } break; default: break; } }; // ... };
内容的提问来源于stack exchange,提问作者mthan
相关产品推荐
相关产品推荐

