使用react-grid-layout拖拽添加组件时尺寸始终为1x1的问题
问题排查与解决方案
核心问题分析
拖拽添加组件尺寸始终为1x1的问题,主要由以下三点原因导致:
1. 重复的布局项唯一ID(i字段)
react-grid-layout要求每个布局项的i属性必须唯一,你当前添加的Grid/Textbox使用固定字符串作为ID,重复ID会导致布局引擎无法正确识别新项,最终重置为默认的1x1尺寸。
2. 渲染条件的变量错误
代码中item.i === Grid和item.i === Textbox的判断里,Grid、Textbox是未定义的变量,而非你添加布局项时使用的字符串"Grid"/"Textbox"。这会导致实际渲染空的<div>,没有内部组件,视觉上呈现为1x1的盒子。
3. 布局更新逻辑冲突
handleLayoutChange中的条件!layoutStore.isDragging && layoutStore.setLayout(layout),会在你设置新布局后触发并覆盖自定义宽高——因为react-grid-layout在drop操作后会自动重新计算布局,此时isDragging已被设为false,导致自定义的w/h被重置。
修复代码示例
1. 修正NewForm组件代码
const ReactGridLayout = WidthProvider(RGL); import { useState } from 'react'; const NewForm = () => { const layoutStore = useLayoutStore(); const [justAdded, setJustAdded] = useState(false); // 标记刚添加的布局项 const onDrop = (layout, item, e) => { layoutStore.setIsDragging(false); if (item && layoutStore.selectedTool) { // 生成唯一ID,避免重复 const newItemId = `${layoutStore.selectedTool}-${Date.now()}`; const newLayout = [...layout]; // 根据选中工具添加对应尺寸的布局项 if (layoutStore.selectedTool === "Grid") { newLayout.push({i: newItemId, x: item.x, y: item.y, w: 3, h: 2}); } else if (layoutStore.selectedTool === "Textbox") { newLayout.push({i: newItemId, x: item.x, y: item.y, w: 2, h: 3}); } layoutStore.setLayout(newLayout); layoutStore.setSelectedTool(null); setJustAdded(true); // 标记刚添加,避免被handleLayoutChange覆盖 } }; const handleLayoutChange = (layout) => { // 刚添加布局项时跳过更新,避免覆盖自定义尺寸 if (!layoutStore.isDragging && !justAdded) { layoutStore.setLayout(layout); } setJustAdded(false); // 重置标记 }; return ( <ReactGridLayout layout={layoutStore.layout} onLayoutChange={handleLayoutChange} cols={12} rowHeight={30} className="layout" onDrop={onDrop} isDroppable={true} compactType={null} > {layoutStore.layout.map((item) => ( // 使用布局项的i作为key,而非index <div key={item.i} style={{border: "1px solid black", textAlign: "center"}}> {/* 修正字符串匹配逻辑 */} {item.i.includes("Grid") && ( <div style={{height: "100%", display: "flex", flexDirection: "column"}}> <label>{item.i.split('-')[0]}</label> <input type="text"/> </div> )} {item.i.includes("Textbox") && ( <Button>random</Button> )} </div> ))} </ReactGridLayout> ); }; export default NewForm;
2. 状态管理代码(可选调整初始布局ID)
const useLayoutStore = create( persist( (set) => ({ // 初始布局也使用唯一ID,避免后续冲突 layout: [{i: "Grid-1", x: 0, y: 0, w: 1, h: 2}, {i: "Textbox-1", x: 1, y: 0, w: 3, h: 2}, {i: "Label-1", x: 4, y: 0, w: 1, h: 2},], selectedTool: null, isDragging: false, setLayout: (newLayout) => set({layout: newLayout}), setSelectedTool: (newTool) => set({selectedTool: newTool}), setIsDragging: (dragging) => set({isDragging: dragging}), }), { name: 'layout-store', getStorage: () => localStorage, })); export default useLayoutStore;
额外注意事项
- 确保外部拖拽源正确设置了
dataTransfer数据,且selectedTool状态在拖拽开始时被正确赋值。 - 避免使用数组
index作为布局项的key,必须使用布局项的唯一i属性,否则React无法正确识别组件更新。
内容的提问来源于stack exchange,提问作者Selim
相关产品推荐
相关产品推荐

