React:如何根据屏幕宽度调整数组映射渲染的元素数量?
问题分析与解决方案
原代码的核心问题
- 初始渲染逻辑错误:直接将
numberToMap初始值设为5,没有判断当前屏幕宽度,导致无论窗口大小,初始都显示5个元素。 - 事件监听堆积:组件每次渲染都会重复添加
change事件监听,窗口调整时会触发大量setNumberToMap调用,直接导致应用冻结。 - 媒体查询区间冲突:
min-width:1194px和max-width:1195px的区间有重叠(1194px-1195px),会导致两个监听同时触发,逻辑混乱。
简便实现方式
方案一:用useEffect管理监听,初始化时修正显示数量
import { useState, useEffect } from 'react'; // ... 其他组件代码 const [numberToMap, setNumberToMap] = useState(5); useEffect(() => { // 定义统一的媒体查询:屏幕宽度小于1194px const mediaQuery = window.matchMedia('(max-width: 1193px)'); // 定义更新逻辑 const updateDisplayCount = () => { setNumberToMap(mediaQuery.matches ? 4 : 5); }; // 初始化时立即执行一次,确保初始显示正确 updateDisplayCount(); // 添加监听 mediaQuery.addEventListener('change', updateDisplayCount); // 组件卸载时清理监听,避免堆积 return () => { mediaQuery.removeEventListener('change', updateDisplayCount); }; }, []); // 空依赖数组,仅在组件挂载时执行一次 const mapAnime = () => ( <div className='anime-list-wrapper'> {animeData?.data?.slice(0, numberToMap).map((anime) => ( <AnimeCard anime={anime} key={anime.id} /> ))} </div> ); return ( <div className='container'> <h3 className='category-title'>{categoryTitle}</h3> {!isFetching && ( <div className="wrapper"> {mapAnime()} </div> )} </div> );
方案二:无需维护state,渲染时直接判断(更简洁)
如果不需要将numberToMap用于其他业务逻辑,可以直接在渲染阶段通过媒体查询结果决定显示数量,省去state的维护成本:
const mapAnime = () => { // 直接判断当前屏幕宽度 const shouldShowFour = window.matchMedia('(max-width: 1193px)').matches; const displayCount = shouldShowFour ? 4 : 5; return ( <div className='anime-list-wrapper'> {animeData?.data?.slice(0, displayCount).map((anime) => ( <AnimeCard anime={anime} key={anime.id} /> ))} </div> ); }; return ( <div className='container'> <h3 className='category-title'>{categoryTitle}</h3> {!isFetching && ( <div className="wrapper"> {mapAnime()} </div> )} </div> );
关键优化点
- 用
useEffect包裹事件监听,确保仅添加一次,组件卸载时自动清理,避免监听堆积导致的性能问题。 - 初始化时主动执行判断逻辑,保证首次渲染的显示数量符合当前屏幕尺寸。
- 合并媒体查询条件,避免区间重叠引发的逻辑冲突。
- 方案二减少了state的使用,代码更简洁直观,适合无额外逻辑依赖的场景。
内容的提问来源于stack exchange,提问作者notoriousRorious
相关产品推荐
相关产品推荐

