React实现仅展示数组3个元素、倒计时结束随机更换元素
React实现列表仅展示3项+倒计时结束自动随机轮换方案
你可以按照以下步骤修改代码,直接落地需求:
核心修改逻辑
- 将全量的5条事件数据抽为固定常量,避免组件每次渲染重新生成id导致本地存储数据异常
- 实现无重复的随机抽取函数,每次从全量列表中拿到3条不重复的展示数据
- 新增独立的本地存储字段保存当前正在展示的3条数据,页面初始化时自动完成首次随机抽取
- 调整倒计时归零回调:重置所有按钮状态的同时,同步重新抽取3条新数据替换当前展示内容
- 优化状态切换逻辑,改用元素唯一id匹配修改项,避免索引错位导致的状态异常
- 渲染时直接遍历当前展示的3条数据,而非全量列表
可直接替换的完整代码
import { useEffect } from 'react'; import { v4 } from 'uuid'; // 按你项目实际的uuid导入路径调整 import useLocalStorage from 'use-local-storage'; // 把全量事件数据抽为组件外固定常量,id固定避免重复生成 const ALL_EVENTS = [ {id: v4(), title: 'Apple iPhone 13 Pro Max 256Gb (небесно-голубой)', avatar: 'https://cdn-icons-png.flaticon.com/512/147/147144.png', statusItem: false}, {id: v4(), title: '500 Stars', avatar: 'https://cdn-icons-png.flaticon.com/512/147/147144.png', statusItem: false}, {id: v4(), title: 'Sony PlayStation 5 Digital Edition', avatar: 'https://cdn-icons-png.flaticon.com/512/147/147144.png', statusItem: false}, {id: v4(), title: 'XBOX 360 Digital Edition', avatar: 'https://cdn-icons-png.flaticon.com/512/147/147144.png', statusItem: false}, {id: v4(), title: 'Google Nexus Digital Edition', avatar: 'https://cdn-icons-png.flaticon.com/512/147/147144.png', statusItem: false} ]; // 随机抽取工具函数:从数组中取n个不重复元素 const getRandomItems = (arr, count = 3) => { const copy = [...arr]; // Fisher-Yates 洗牌算法打乱数组 for (let i = copy.length - 1; i > 0; i--) { const randomIndex = Math.floor(Math.random() * (i + 1)); [copy[i], copy[randomIndex]] = [copy[randomIndex], copy[i]]; } // 重置所有项的选中状态后返回前count条 return copy.slice(0, count).map(item => ({...item, statusItem: false})); }; const CurrentEventsItem = () => { const [timeLeft, setTimeLeft] = useLocalStorage('timer', 5 * 60); // 新增本地存储字段,保存当前展示的3条数据,初始化时自动随机抽取 const [displayList, setDisplayList] = useLocalStorage('currentDisplayEvents', getRandomItems(ALL_EVENTS)); const getPadTime = (time) => time.toString().padStart(2, '0'); const minutes = getPadTime(Math.floor(timeLeft / 60)); const seconds = getPadTime(timeLeft - minutes * 60); useEffect(() => { const interval = setInterval(() => { setTimeLeft((timeLeft) => (timeLeft >= 1 ? timeLeft - 1 : resetAndRefresh() || 5 * 60)); }, 1000); return () => clearInterval(interval); }, []); // 优化状态切换逻辑,用id匹配避免索引错位 const toggleActive = (id) => { const updatedList = displayList.map(item => item.id === id ? {...item, statusItem: !item.statusItem} : item ); setDisplayList(updatedList); }; const toggleActiveStyles = (status) => { return status ? 'current__events__hot-price disabled' : 'current__events__hot-price'; }; const toggleActiveStylesBtns = (status) => { return status ? 'current__events__btn-green disabled' : 'current__events__btn-green'; }; // 倒计时归零的回调:重置状态+刷新展示列表 const resetAndRefresh = () => { // 重新抽取3条新数据更新展示 setDisplayList(getRandomItems(ALL_EVENTS)); }; return ( <> <div className='current__events__wrapper'> {/* 直接遍历当前展示的3条数据渲染 */} {displayList.map((item) => <div className="current__events__hot-price__item" key={item.id}> <div className={toggleActiveStyles(item.statusItem)}> <h5 className="current__events__card-title__large">Hot Price</h5> </div> <div className="current__events__image"> <img src={item.avatar} alt='user' className="rounded-circle" width='75' height='75'/> </div> <div className="current__events__info"> <h4 className="current__events__title__middle">{item.title}</h4> </div> <div className="current__events__timer"> <span>{minutes}</span> <span>:</span> <span>{seconds}</span> </div> <button className={toggleActiveStylesBtns(item.statusItem)} onClick={() => toggleActive(item.id)} disabled={item.statusItem} > СДЕЛАТЬ ХОД </button> </div> )} </div> </> ) } export default CurrentEventsItem;
关键注意点
- 原代码把全量数据存在localStorage中,每次组件初始化都会重新生成uuid,会导致本地存储的旧数据和新数据id不匹配,抽成外部常量后id只在首次加载时生成一次,稳定性更高
- 随机抽取使用洗牌算法实现,不会出现同一次展示3条内容重复的问题
- 改用id作为列表key和状态匹配依据,避免因为列表顺序变化、索引错位导致的样式、点击事件异常
- 每次抽取新的展示数据时会自动重置所有项的
statusItem为false,不需要额外遍历重置按钮状态
内容的提问来源于stack exchange,提问作者Eugene Kliuchnikov
相关产品推荐
相关产品推荐

