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

拖拽触发Maximum update depth exceeded警告的解决问询

解决Grid拖拽至Scheduler时的"Maximum update depth exceeded"错误

问题描述

实现了Grid条目拖拽到Scheduler的功能,功能正常,但添加第6个条目并多次拖拽后,出现Warning: Maximum update depth exceeded错误。将useEffect依赖设为[dragItem]后错误消失,但拖拽功能无法正常工作。

App组件代码

import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Scheduler,
    WeekView,
    MonthView
} from "@progress/kendo-react-scheduler";
import { guid } from '@progress/kendo-react-common';

import { Grid, GridColumn } from "@progress/kendo-react-grid";

import gridData from "./data.js";

const App = () => {
    const MyScheduler = React.createRef();
    const [data, setData] = React.useState([]);
    const [dragTitle, setDragTitle] = React.useState("");
    const [dragItem, setDragItem] = React.useState("");
    const handleDropItem = e => {
        let start = e.target.getAttribute("data-slot-start");
        let end = e.target.getAttribute("data-slot-end");
        let startDate = new Date(parseInt(start));
        let endDate = new Date(parseInt(end));
        let newEvent = {
            id: guid(),
            title: dragItem.title,
            StartTimezone: null,
            start: startDate,
            end: endDate
        };
        setData([newEvent, ...data]);
    };
    React.useEffect(() => {
        let schedulerElement = MyScheduler.current.element;
        schedulerElement.addEventListener("drop", handleDropItem);
        schedulerElement.addEventListener("dragover", e => e.preventDefault());
    });

    const GridRowRender = (tr, props) => {
        const trProps = {
            draggable: true,
            onDragStart: e => {
                setDragItem(props.dataItem)
            }
        };
        return React.cloneElement(tr, { ...trProps }, tr.props.children);
    };
    return (
        <>
            <Scheduler data={data} defaultDate={new Date("2013/6/13")} ref={MyScheduler}>
                <WeekView showWorkHours={false} />
                <MonthView />
            </Scheduler>
            <hr />
            <Grid data={gridData} rowRender={GridRowRender}>
                <GridColumn field='taskID' />
                <GridColumn field='title' />
            </Grid>
        </>
    );
};

ReactDOM.render(<App />, document.querySelector("my-app"));

data.js代码

const data = [
  {
    taskID: 1,
    ownerId: 1,
    title: 'Fast and furious 6',
  },
  {
    taskID: 2,
    ownerId: 3,
    title: 'The Internship',
  },
  {
    taskID: 3,
    ownerId: 2,
    title: 'The Perks of Being a Wallflower',
  },
  {
    taskID: 4,
    ownerId: 1,
    title: 'The Help',
  },
  {
    taskID: 5,
    ownerId: 2,
    title: 'Now You See Me',
  },
  {
    taskID: 6,
    ownerId: 2,
    title: 'Sixth Item',
  },
];

export default data;

错误原因分析

  1. 重复绑定事件监听器:useEffect未设置依赖数组和清理函数,组件每次渲染(如data更新、dragItem变化)都会给Scheduler元素重复添加drop和dragover监听器。多次拖拽后,单次drop操作会触发多个handleDropItem调用,频繁触发状态更新,最终超出React更新深度限制。
  2. 状态更新依赖旧值:handleDropItem直接使用data状态,闭包特性导致函数捕获渲染时的旧data值,可能引发状态更新异常,加剧重复渲染。
  3. 不必要的组件重渲染:GridRowRender和handleDropItem每次渲染都会重新创建,导致Grid行和事件监听器频繁更新,增加渲染压力。

解决方案

以下修改可彻底解决错误并保持功能正常:

import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Scheduler,
    WeekView,
    MonthView
} from "@progress/kendo-react-scheduler";
import { guid } from '@progress/kendo-react-common';

import { Grid, GridColumn } from "@progress/kendo-react-grid";

import gridData from "./data.js";

const App = () => {
    const MyScheduler = React.createRef();
    const [data, setData] = React.useState([]);
    const [dragItem, setDragItem] = React.useState(null);

    // 用useCallback稳定函数引用,依赖dragItem确保获取最新拖拽项
    const handleDropItem = React.useCallback((e) => {
        if (!dragItem) return;
        const start = e.target.getAttribute("data-slot-start");
        const end = e.target.getAttribute("data-slot-end");
        if (!start || !end) return;

        const startDate = new Date(parseInt(start));
        const endDate = new Date(parseInt(end));
        const newEvent = {
            id: guid(),
            title: dragItem.title,
            StartTimezone: null,
            start: startDate,
            end: endDate
        };
        // 函数式更新获取最新data状态
        setData(prevData => [newEvent, ...prevData]);
    }, [dragItem]);

    // 用useCallback稳定GridRowRender引用,避免不必要的行重渲染
    const GridRowRender = React.useCallback((tr, props) => {
        const trProps = {
            draggable: true,
            onDragStart: () => {
                setDragItem(props.dataItem);
            }
        };
        return React.cloneElement(tr, trProps, tr.props.children);
    }, []);

    React.useEffect(() => {
        const schedulerElement = MyScheduler.current?.element;
        if (!schedulerElement) return;

        const handleDragOver = (e) => e.preventDefault();
        schedulerElement.addEventListener("drop", handleDropItem);
        schedulerElement.addEventListener("dragover", handleDragOver);

        // 清理函数:移除事件监听器,避免内存泄漏和重复绑定
        return () => {
            schedulerElement.removeEventListener("drop", handleDropItem);
            schedulerElement.removeEventListener("dragover", handleDragOver);
        };
    }, [handleDropItem]);

    return (
        <>
            <Scheduler data={data} defaultDate={new Date("2013/6/13")} ref={MyScheduler}>
                <WeekView showWorkHours={false} />
                <MonthView />
            </Scheduler>
            <hr />
            <Grid data={gridData} rowRender={GridRowRender}>
                <GridColumn field='taskID' />
                <GridColumn field='title' />
            </Grid>
        </>
    );
};

ReactDOM.render(<App />, document.querySelector("my-app"));

修改要点说明

  • 添加事件清理逻辑:在useEffect返回函数中移除监听器,避免重复绑定导致的多次触发。
  • 使用useCallback稳定引用:确保handleDropItem和GridRowRender的引用在依赖不变时保持稳定,减少不必要的重渲染和事件绑定。
  • 函数式状态更新:setData(prevData => [...])确保每次更新基于最新状态,避免闭包导致的旧值问题。
  • 增加边界判断:在handleDropItem中检查拖拽项和时间槽参数,避免无效操作。

内容的提问来源于stack exchange,提问作者Murray Johnson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:05:10