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

ReactGridLayout报x must be a number错误排查与函数组件实现

错误根源

报错ReactGridLayout.children[0].x must be a number是多个逻辑错误叠加导致的,第一次加项时初始布局为空所以没触发校验,第二次加项时非法布局数据被回写到状态就直接触发报错:

  • 初始化布局逻辑错误:构造函数里遍历savedLayout.lg时,map回调的第一个参数i是布局项对象而非索引,x: i * 2的计算结果是NaN
  • 子元素属性传错:createElement渲染子项时给data-grid传的是数组索引(纯数字),RGL读取子项布局配置时拿不到合法的x/y/w/h字段
  • 缓存逻辑错误:开头直接执行localStorage.setItem("username")未传存储值,会把username字段存为null,后续所有布局缓存的读写key都是null,会导致缓存混乱
  • 依赖缺失:代码用了_.map、_.reject但未引入lodash,本身就存在引用错误
  • RGL配置缺失:使用响应式模式的RGL组件时,只传了当前layout,没传全量layouts、断点、列数配置,状态同步逻辑不完整
修复后函数组件实现

已经把所有逻辑错误修复,完全采用函数组件+Hooks写法,保留瓦片增删、本地缓存持久化功能:

import React, { useState, useEffect, useCallback } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import { v4 as uuid } from "uuid";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";

const ReactGridLayout = WidthProvider(Responsive);
// 响应式断点配置
const BREAKPOINTS = { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 };
// 各断点对应列数
const COLS = { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 };
// 本地缓存key,可替换为实际登录用户名
const STORAGE_KEY = "rgl_dash_layout";

export default function Dash() {
  // 存储全断点布局配置
  const [layouts, setLayouts] = useState({ lg: [] });
  const [currentBreakpoint, setCurrentBreakpoint] = useState("lg");

  // 初始化读取本地缓存
  useEffect(() => {
    const saved = localStorage.getItem(STORAGE_KEY);
    if (saved) {
      try {
        const savedLayouts = JSON.parse(saved);
        setLayouts(savedLayouts);
      } catch (e) {
        console.error("布局缓存读取失败", e);
      }
    }
  }, []);

  // 布局变更时同步状态+持久化
  const onLayoutChange = useCallback((currentLayout, allLayouts) => {
    setLayouts(allLayouts);
    localStorage.setItem(STORAGE_KEY, JSON.stringify(allLayouts));
  }, []);

  // 新增瓦片
  const addItem = useCallback(() => {
    const newId = uuid();
    setLayouts(prev => {
      const currentLayout = prev[currentBreakpoint] || [];
      const newItem = {
        i: newId,
        x: (currentLayout.length * 2) % (COLS[currentBreakpoint] || 12),
        y: Infinity, // 自动放到最底部
        w: 2,
        h: 2
      };
      return {
        ...prev,
        [currentBreakpoint]: [...currentLayout, newItem]
      };
    });
  }, [currentBreakpoint]);

  // 删除瓦片
  const removeItem = useCallback((itemId) => {
    setLayouts(prev => {
      const currentLayout = prev[currentBreakpoint] || [];
      return {
        ...prev,
        [currentBreakpoint]: currentLayout.filter(item => item.i !== itemId)
      };
    });
  }, [currentBreakpoint]);

  // 断点切换
  const onBreakpointChange = useCallback((breakpoint) => {
    setCurrentBreakpoint(breakpoint);
  }, []);

  return (
    <div id="maincontent" className="content">
      <button onClick={addItem} style={{ marginBottom: 16 }}>
        新增瓦片
      </button>
      <ReactGridLayout
        className="layout"
        resizeHandles={['se']}
        compactType="vertical"
        draggableHandle=".dragHandle"
        breakpoints={BREAKPOINTS}
        cols={COLS}
        layouts={layouts}
        onBreakpointChange={onBreakpointChange}
        onLayoutChange={onLayoutChange}
      >
        {(layouts[currentBreakpoint] || []).map(elem => (
          <div key={elem.i} className="dragHandle" style={{ border: "1px solid #ddd", padding: 10, background: "#fff", position: "relative" }}>
            <span
              style={{
                position: "absolute",
                right: "2px",
                top: 0,
                cursor: "pointer"
              }}
              onClick={() => removeItem(elem.i)}
            >
              x
            </span>
            瓦片内容 {elem.i.slice(0,4)}
          </div>
        ))}
      </ReactGridLayout>
    </div>
  );
}
关键修复点说明
  • 去掉了错误的初始化布局遍历逻辑,初始化直接读取缓存的全量断点布局
  • 不再给子元素传错误的data-grid属性,统一通过RGL的layoutsprop传配置,避免属性读取错误
  • 修正了localStorage读写逻辑,固定缓存key避免存null的问题
  • 去掉了未引入的lodash依赖,全部用原生数组方法实现
  • 补全了响应式RGL必需的断点、列数配置,切换断点时自动同步布局状态
  • 新增瓦片时y坐标设为Infinity,会自动把新瓦片放到现有布局最底部,不会覆盖已有内容

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 16:51:25