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

如何程序化更新react-grid-layout组件的w与h属性?

问题描述

我需要实现点击按钮即可将特定grid item的尺寸设置为指定值(比如宽3、高1),不想通过拖拽手动调整。查阅官方文档后没找到对应的实现方法,自己尝试了一种方案,但组件重渲染时会出现明显的闪烁问题。

代码示例

export default function BentoCards() {
  const items = [
    { i: "a", x: 0, y: 0, w: 2, h: 1 },
    { i: "b", x: 2, y: 4, w: 2, h: 1 },
  ];

  const [stateLayout, setStateLayout] = useState(items);

  const ResponsiveGridLayout = WidthProvider(Responsive);
  const gridLayoutRef = useRef<React.MutableRefObject<any>>(null);
  const gridLayouts = Array.from({ length: items.length }, () => useRef(null));

  const testChangeHProgrammatically = () => {
    setStateLayout((prevData) => {
      const newData = [...prevData]; // 拷贝数组
      newData[0].h = 3; // 更新指定元素
      return newData; // 返回更新后的数组
    });
  };

  return (
    <>
      <ResponsiveGridLayout
        className="layout"
        breakpoints={{ sm: 768, xs: 480 }}
        cols={{ sm: 6, xs: 2 }}
        resizeHandles={["se"]}
        layouts={{ sm: stateLayout, xs: stateLayout }}
      >
        {stateLayout.map((item, index: number) => {
          console.log("re-render");
          return (
            <CustomGridItemComponent
              key={item.i}
              className="bg-red-500"
              data-grid={{ ...item }}
              ref={gridLayouts[index]}
            >
              Hello World
              <Resizable key={item.i} handleAxis="s"></Resizable>
            </CustomGridItemComponent>
          );
        })}
      </ResponsiveGridLayout>
      <button onClick={testChangeHProgrammatically}>Change Size</button>
    </>
  );
}
解决方案

闪烁问题核心是不必要的组件重渲染和状态更新逻辑不严谨,以下是针对性优化方案:

1. 用React.memo优化子组件

CustomGridItemComponent会在布局更新时无条件重渲染,导致视觉闪烁。用React.memo包裹子组件,仅在props实际变化时触发重渲染:

import React from 'react';

const CustomGridItemComponent = React.memo(({ className, dataGrid, children }) => {
  return (
    <div className={className} data-grid={dataGrid}>
      {children}
      <Resizable handleAxis="s" />
    </div>
  );
}, (prevProps, nextProps) => {
  // 自定义比较逻辑,对比dataGrid的属性值而非引用
  const prevGrid = prevProps.dataGrid;
  const nextGrid = nextProps.dataGrid;
  return prevGrid.i === nextGrid.i &&
         prevGrid.x === nextGrid.x &&
         prevGrid.y === nextGrid.y &&
         prevGrid.w === nextGrid.w &&
         prevGrid.h === nextGrid.h;
});

2. 正确更新布局状态

当前状态更新仅浅拷贝数组并修改原对象属性,可能导致React无法精准识别变化。应为修改的item创建新对象,保持未修改item的引用稳定:

const testChangeHProgrammatically = () => {
  setStateLayout(prevData => 
    prevData.map((item, index) => 
      index === 0 ? { ...item, h: 3 } : item
    )
  );
};

这种写法既更新了目标item的属性,又保证未修改的item引用不变,配合子组件的memo可大幅减少重渲染次数。

3. 直接调用grid布局实例的API

如果上述方法仍有闪烁,可尝试直接调用react-grid-layout的内置API更新布局,减少React渲染层的波动:

// 修正ref类型
const gridLayoutRef = useRef<InstanceType<typeof ResponsiveGridLayout>>(null);

const testChangeHProgrammatically = () => {
  if (!gridLayoutRef.current) return;
  
  const newLayout = stateLayout.map((item, index) => 
    index === 0 ? { ...item, h: 3 } : item
  );
  
  // 直接调用实例方法更新布局
  gridLayoutRef.current.updateLayout(newLayout);
  // 同步更新state保持数据一致性
  setStateLayout(newLayout);
};

// 在组件中绑定ref
<ResponsiveGridLayout
  ref={gridLayoutRef}
  // 其他props...
>

4. 优化Resizable组件的渲染

避免在循环中频繁创建Resizable组件的新实例,将其整合到CustomGridItemComponent内部,配合memo一起优化,减少不必要的DOM操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:31:00