React中Masonic布局侧边栏关闭后列无法自适应的解决方法
问题:Masonic瀑布流布局侧边栏切换后列数无法自适应
在React项目中使用Masonic库实现瀑布流布局(该库在同类工具中性能最优),但遇到侧边栏切换适配问题:打开侧边栏时,列数能正常适配容器宽度;关闭侧边栏后,列数无法重新调整以利用新增的容器空间。效果如下:

代码示例:
import React, { useState, useEffect} from 'react' import { Masonry, } from 'masonic'; import { useWindowSize } from '@react-hook/window-size'; import '../MasonryGridLayout.css'; export const GrideView = () => { const [isMobile, setIsMobile] = useState(false) const [refHeight, setheight] = useState(null) const [sideBar, setSideBar] = useState(false) const [windowWidth, height] = useWindowSize() const handleSidebar = () => { setSideBar(!sideBar) if (ref.current) { console.log('current') } } useEffect(() => { const handleMobile = () => { if (window.innerWidth <= 720) { setIsMobile(true) } else { setIsMobile(false) } } handleMobile() }, []) return ( <div style={{ display: 'flex', flexDirection: 'column' }}> <div className='header-style'> <button style={{ width: 'auto', height: '20px', backgroundColor: 'teal', color: 'white', marginLeft: '20px', }} onClick={handleSidebar} > toggle Sidebar </button> <button style={{ width: 'auto', height: '20px', backgroundColor: 'tan', color: 'white', marginLeft: '20px', }} > Upload </button> </div> <div className="wrapper"> { isMobile ? null : sideBar ? <div className='side-bar-style'> this is a side bar </div> : null } <div className='masonic' style={{ height: `${refHeight}` }}> <Masonry // Provides the data for our grid items items={items} // Adds 8px of space between the grid cells columnGutter={8} maxColumnCount={sideBar ? 7 : 8} columnCount={windowWidth < 600 && 2} // Sets the minimum column width to 172px columnWidth={172} // Pre-renders 5 windows worth of content overscanBy={2} // This is the grid item component render={FakeCard} /> </div> </div> </div> ); } const FakeCard = ({ data: { postID, post, description } }) => ( <div className='card-style' style={{ height: `${description}` }}> <img className='img-style' alt="kitty" src={post} /> </div> );
解决方案
问题根源
Masonic默认依赖窗口尺寸变化触发列数重新计算,但侧边栏切换属于容器内部宽度变化(窗口尺寸未改变),useWindowSize无法监听到该变化,导致Masonic不会自动调整列数。
解决方法
方法1:添加动态key强制重渲染
当侧边栏状态变化时,修改Masonic组件的key,让React重新挂载组件,触发Masonic重新计算列数。
修改Masonic组件部分:
<Masonry key={sideBar} // 新增:侧边栏状态变化时强制重渲染 items={items} columnGutter={8} maxColumnCount={sideBar ? 7 : 8} columnCount={windowWidth < 600 ? 2 : undefined} // 修正逻辑:窗口宽度≥600时让Masonic自动计算 columnWidth={172} overscanBy={2} render={FakeCard} />
方法2:监听容器宽度变化(更优雅)
使用useResizeObserver钩子监听Masonic容器的宽度变化,当容器宽度改变时触发列数重新计算。
- 安装依赖:
npm install @react-hook/resize-observer
- 修改后的完整代码:
import React, { useState, useEffect, useRef } from 'react' import { Masonry } from 'masonic'; import { useWindowSize } from '@react-hook/window-size'; import { useResizeObserver } from '@react-hook/resize-observer'; import '../MasonryGridLayout.css'; export const GrideView = () => { const [isMobile, setIsMobile] = useState(false) const [refHeight, setheight] = useState(null) const [sideBar, setSideBar] = useState(false) const [windowWidth, height] = useWindowSize() const masonryContainerRef = useRef(null) const [containerWidth, setContainerWidth] = useState(window.innerWidth) // 监听容器宽度变化 useResizeObserver(masonryContainerRef, (entry) => { setContainerWidth(entry.contentRect.width) }) const handleSidebar = () => { setSideBar(!sideBar) } useEffect(() => { const handleMobile = () => { setIsMobile(window.innerWidth <= 720) } handleMobile() // 添加窗口resize监听并清理 window.addEventListener('resize', handleMobile) return () => window.removeEventListener('resize', handleMobile) }, []) return ( <div style={{ display: 'flex', flexDirection: 'column' }}> <div className='header-style'> <button style={{ width: 'auto', height: '20px', backgroundColor: 'teal', color: 'white', marginLeft: '20px', }} onClick={handleSidebar} > toggle Sidebar </button> <button style={{ width: 'auto', height: '20px', backgroundColor: 'tan', color: 'white', marginLeft: '20px', }}> Upload </button> </div> <div className="wrapper"> {!isMobile && sideBar && <div className='side-bar-style'>this is a side bar</div>} <div className='masonic' style={{ height: refHeight }} ref={masonryContainerRef}> <Masonry items={items} columnGutter={8} maxColumnCount={sideBar ? 7 : 8} columnCount={windowWidth < 600 ? 2 : undefined} columnWidth={172} overscanBy={2} render={FakeCard} /> </div> </div> </div> ); } const FakeCard = ({ data: { postID, post, description } }) => ( <div className='card-style' style={{ height: `${description}` }}> <img className='img-style' alt="kitty" src={post} /> </div> );
额外优化点
- 修正
columnCount逻辑:原代码中windowWidth < 600 && 2在窗口宽度≥600时会返回false,导致Masonic无法自动计算列数,改为windowWidth < 600 ? 2 : undefined。 - 给移动端监听添加清理函数,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Jovane Mullings
相关产品推荐
相关产品推荐

