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

react-grid-layout嵌套拖拽异常:三级网格拖拽后主网格无法拖拽修复

解决多层嵌套react-grid-layout拖拽后父网格无法操作的问题

问题根源

你遇到的问题是因为每个CustomLayout组件维护自己独立的isDraggable状态,拖拽嵌套层级的元素时,状态更新无法向上传递到所有祖先组件。例如拖拽三级网格后,二级组件的状态会被重置,但最顶层组件的isDraggable状态仍保持为false,必须再次拖拽二级组件才能触发顶层状态的重置。

解决方案:使用全局状态跟踪拖拽状态

我们可以通过React Context实现全局的拖拽状态跟踪,让所有层级的组件共享同一个拖拽状态,确保任意层级的拖拽操作都能同步更新所有祖先组件的可拖拽状态。

步骤1:创建拖拽状态Context

首先创建一个Context来管理全局的拖拽计数和状态:

import { createContext, useContext, useState, useCallback } from "react";

const DragContext = createContext({
  isDragging: false,
  startDrag: () => {},
  stopDrag: () => {}
});

export function DragProvider({ children }) {
  const [dragCount, setDragCount] = useState(0);

  const startDrag = useCallback(() => {
    setDragCount(prev => prev + 1);
  }, []);

  const stopDrag = useCallback(() => {
    setDragCount(prev => Math.max(prev - 1, 0));
  }, []);

  return (
    <DragContext.Provider value={{
      isDragging: dragCount > 0,
      startDrag,
      stopDrag
    }}>
      {children}
    </DragContext.Provider>
  );
}

步骤2:修改CustomLayout组件使用Context

更新CustomLayout组件,移除本地状态,改用Context提供的全局拖拽状态:

import "./styles.css";
import GridLayout from "react-grid-layout";
import { useContext } from "react";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";
import { DragContext } from "./DragProvider";

export default function CustomLayout({ layout }) {
  const { isDragging, startDrag, stopDrag } = useContext(DragContext);

  return (
    <GridLayout
      className="layout"
      onDragStart={() => {
        if (!isDragging) {
          startDrag();
        }
      }}
      onDragStop={stopDrag}
      layout={layout}
      cols={24}
      rowHeight={80}
      width={1600}
      isDraggable={!isDragging}
    >
      {layout.map((item) => {
        if (item.children) {
          return (
            <div
              key={item.i}
              style={{ background: "pink", border: "1px solid black" }}
            >
              <CustomLayout layout={item.children} />
            </div>
          );
        } else {
          return (
            <div key={item.i} style={{ background: "purple" }}>
              {item.i}
            </div>
          );
        }
      })}
    </GridLayout>
  );
}

步骤3:在顶层组件中使用DragProvider

最后,在使用CustomLayout的顶层组件中,用DragProvider包裹它:

import CustomLayout from "./CustomLayout";
import { DragProvider } from "./DragProvider";

function App() {
  // 替换为你自己的布局数据
  const initialLayout = [
    { i: "1", x: 0, y: 0, w: 12, h: 2, children: [
      { i: "1-1", x: 0, y: 0, w: 12, h: 2, children: [
        { i: "1-1-1", x: 0, y: 0, w: 6, h: 2 },
        { i: "1-1-2", x: 6, y: 0, w: 6, h: 2 }
      ]},
      { i: "1-2", x: 0, y: 2, w: 12, h: 2 }
    ]},
    { i: "2", x: 12, y: 0, w: 12, h: 4 }
  ];

  return (
    <DragProvider>
      <CustomLayout layout={initialLayout} />
    </DragProvider>
  );
}

export default App;

修复原理

  • 全局拖拽计数:通过dragCount跟踪当前活跃的拖拽操作数量,任意层级的拖拽开始/结束都会同步更新计数。
  • 统一可拖拽状态:所有CustomLayout组件的isDraggable由全局isDragging状态控制,只有当没有任何拖拽操作时,网格才允许拖拽。
  • 状态自动同步:拖拽结束后计数归零,所有层级的网格自动恢复可拖拽状态,无需额外操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 20:41:05