如何程序化更新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
相关产品推荐
相关产品推荐

