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

如何通过useEffect与resize事件替换数组现有值,避免持续追加

解决窗口Resize时React状态数组无法替换只能追加的问题

你这代码里有两个关键问题导致resize时状态只会往数组里追加值,没法替换:

  1. resize回调里用了setStart((start) => [...start, newVal]),这是在原数组后面加新元素,不是替换现有内容
  2. 每次shifts更新时都会给window加新的resize监听,旧监听没清理,不仅会重复触发,而且回调里捕获的hour、childPosStart都是初始渲染时的旧值,resize后DOM位置变了计算结果也不对

给你改好的代码,直接用就行:

useEffect(() => {
  // 把计算逻辑抽成函数,初始和resize时都能调用
  const calculatePositions = () => {
    const hour = [...hourRef.current.children];
    const parentPos = parentWidthRef.current.getBoundingClientRect();
    const hourWidth = Math.round(hour[0].getBoundingClientRect().width);

    // 每次重新生成完整的start和end数组
    const newStart = [];
    const newEnd = [];

    shifts.forEach((shift) => {
      const startHour = parseInt(shift.from.slice(0, 2));
      const endHour = parseInt(shift.to.slice(0, 2));
      const startMinute = parseInt(shift.from.slice(3, 5));
      const endMinute = parseInt(shift.to.slice(3, 5));

      const startEl = hour.find(h => h.getAttribute('data-id') === startHour.toString());
      const endEl = hour.find(h => h.getAttribute('data-id') === endHour.toString());

      // 加个判断防止找不到元素报错
      if (startEl && endEl) {
        const childPosStart = startEl.getBoundingClientRect();
        const childPosEnd = endEl.getBoundingClientRect();

        newStart.push(Math.round((childPosStart.left - parentPos.left) + ((startMinute * hourWidth) / 60)));
        newEnd.push(Math.round((childPosEnd.left - parentPos.left) + ((endMinute * hourWidth) / 60)));
      }
    });

    // 直接用新数组替换旧状态,不是追加
    setStart(newStart);
    setEnd(newEnd);
  };

  // 页面加载完先算一次初始位置
  calculatePositions();

  // 绑定resize监听
  window.addEventListener('resize', calculatePositions);

  // 组件卸载或者shifts变化时,移除监听避免内存泄漏
  return () => {
    window.removeEventListener('resize', calculatePositions);
    setStart([]);
    setEnd([]);
  };
}, [shifts]);

重点说下改了啥:

  • 把位置计算逻辑单独拎出来,每次resize时重新获取最新的DOM位置和宽度,不会再用旧的闭包值
  • 每次计算都生成全新的数组,直接替换掉原来的状态,彻底解决追加的问题
  • 给resize监听加了清理逻辑,不会重复绑定导致多次触发
  • 加了元素存在性判断,避免找不到对应hour元素时抛出错误
  • 把时分的解析拆出来,代码可读性更好

内容的提问来源于stack exchange,提问作者Martin Avramov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:25:33