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

未使用useEffect却触发Maximum update depth exceeded警告求助

解决Maximum update depth exceeded警告(仅开发者工具打开时触发)

问题描述

触发Maximum update depth exceeded警告,且仅在浏览器开发者工具打开时出现,代码中未使用会引发该问题的useEffect。

问题根源

  1. useSelector比较逻辑缺陷:使用引用相等(===)比较对象类型的stageAttrs,而Redux状态更新时即使对象内容未变,引用变化也会导致useSelector判定状态改变,触发组件不必要重渲染。当开发者工具打开时,Redux DevTools的状态监听会放大这个问题,结合onMouseMove的持续dispatch,形成更新循环。
  2. Redux reducer违反immutable原则:mouseMove reducer中直接修改原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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 17:16:13