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
相关产品推荐
相关产品推荐

