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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 20:00:13