如何优化React条件渲染模态框,避免容器展开时尺寸变化?
优化方案与问题修复
1. 修复useEffect无限循环问题
你当前的useEffect依赖了hasDetail,但内部又调用setHasDetail(true),这会触发无限重渲染循环——每次设置hasDetail都会重新执行useEffect,进而再次设置状态,陷入死循环。
修改方案:移除hasDetail作为依赖项,直接通过focusItem?.hasDetail判断,无需额外维护hasDetail状态:
useEffect(() => { if (isModalOpen && focusItem?.hasDetail) { const arr: string[] = focusItem.detailPicture?.split(";") || []; arr.unshift(focusItem.pictureLnk); setDetail(arr); } else if (isModalOpen) { setDetail([]); // 清空轮播图数据 } }, [isModalOpen, focusItem]);
2. 简化条件渲染逻辑
不需要单独维护hasDetail状态,直接通过detail.length > 0或focusItem?.hasDetail判断渲染模式,减少状态变量的同时避免不必要的重渲染。
重构为组件拆分模式
把轮播和单图拆成独立组件,代码更清晰,还能通过React.memo缓存组件,避免父组件重渲染时无意义的子组件更新:
// 父组件中 <div className="modalCont"> {detail.length > 0 ? ( <CarouselModal detail={detail} carouselIndex={carouselIndex} onArrowChange={handleArrowChange} /> ) : ( <SingleImageModal focusItem={focusItem} /> )} </div> // 轮播组件(缓存优化) const CarouselModal = React.memo(({ detail, carouselIndex, onArrowChange }) => { const arrowDecrement = () => onArrowChange(-1); const arrowIncrement = () => onArrowChange(1); return ( <div className="carouselModalImgWrap"> <div className="carouselArrwContLeft"> <Left className={`carouselArrw ${carouselIndex === 0 ? "arrwDisabled" : ""}`} onClick={arrowDecrement} /> </div> <div className="carouselImgCont"> {detail.map((url, i) => ( <div key={`carousel-img-${i}`} // 用索引+前缀做key,避免重复URL的问题 className="carouselImgFit" style={{ translate: `${-100 * carouselIndex}%`, transition: "300ms translate ease-in-out", }} > <img className={`modalImg ${carouselIndex === i ? "" : "hideModalImg"}`} src={url} loading="lazy" // 开启懒加载 /> </div> ))} </div> <div className="carouselImgPrvwWrap hasWrap"> {detail.map((url, i) => ( <div key={`preview-img-${i}`} className={`carouselImgPrvCont ${carouselIndex === i ? "carouselImgPrevActive" : ""}`} > <img src={url} className="carouselImgPrv" loading="lazy" /> </div> ))} </div> <div className="carouselArrwContRight"> <Right className={`carouselArrw ${carouselIndex === detail.length - 1 ? "arrwDisabled" : ""}`} onClick={arrowIncrement} /> </div> </div> ); }); // 单图组件(缓存优化) const SingleImageModal = React.memo(({ focusItem }) => ( <div className="modalImgWrap"> <div className="modalImgCont"> <img className="modalImg" loading="lazy" src={focusItem?.pictureLnk} /> </div> {/* 无详情时用样式控制高度为0,保持布局一致 */} <div className="carouselImgPrvwWrap nowWrap"></div> </div> ));
3. 预览栏的优化
针对预览栏高度控制的问题,可做以下优化:
- 无需在两种模式下都保留空DOM节点,单图模式下如果不需要预览内容,直接用
className="nowWrap"设置高度为0即可; - 避免重复的className判断,直接通过
detail.length > 0切换样式:
<div className={`carouselImgPrvwWrap ${detail.length > 0 ? "hasWrap" : "nowWrap"}`}> {detail.length > 0 && detail.map((url, i) => ( <div key={`preview-img-${i}`} className={`carouselImgPrvCont ${carouselIndex === i ? "carouselImgPrevActive" : ""}`} > <img src={url} className="carouselImgPrv" /> </div> ))} </div>
4. 提升渲染速度的额外建议
- 图片加载优化:给所有图片添加
loading="lazy",轮播图可仅加载当前显示项及前后各一张,其余项动态设置src(监听carouselIndex变化实现); - 减少重渲染:用
useCallback包裹箭头函数(如arrowDecrement),避免因函数引用变化触发子组件重渲染; - 简化DOM结构:轮播图可仅渲染当前显示的图片,非当前项用
display: none隐藏,减少DOM节点数量; - 状态精简:用
detail数组长度替代hasDetail状态,减少state数量,降低重渲染触发概率。
内容的提问来源于stack exchange,提问作者spencer
相关产品推荐
相关产品推荐

