React音乐播放器洗牌功能报错:更新共享state时无法读取title
音乐播放器洗牌功能报错修复
场景说明
我正在开发一款音乐播放器,组件结构如下:
- A:Home.jsx(首页组件)
- B:ChartSongs.jsx(榜单歌曲组件)
- C:AudioPlayer.jsx(音频播放器组件)
在Home组件中,我通过接口获取了歌曲数组chartData,并将其作为props传递给ChartSongs组件:
// chartData是从接口获取的歌曲数组 const songTitleEl = chartData.map((data, i) => ( <ChartSongs key={data.key} title={data.title} artiste={data.subtitle} coverarts={data.images?.coverart} index={i} /> ))
我使用Context API在组件间共享chartData,并在AudioPlayer组件中实现了Fisher-Yates洗牌算法用于打乱歌曲顺序:
function shuffle(arr){ for (let i = arr.length; i > 0; i--){ const j = Math.floor(Math.random() * (i + 1)); const temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } return arr; }
随后编写了更新状态的函数:
function playShuffle(){ setChartData(shuffle(chartData)) }
报错信息
调用playShuffle()时出现如下错误:
Uncaught TypeError: Cannot read properties of undefined (reading 'title') at Home.jsx:74:44 at Array.map (<anonymous>) at Home (Home.jsx:73:33) at renderWithHooks (react-dom.development.js:16305:18) at updateFunctionComponent (react-dom.development.js:19588:20) at beginWork (react-dom.development.js:21601:16) at beginWork$1 (react-dom.development.js:27426:14) at performUnitOfWork (react-dom.development.js:26557:12) at workLoopSync (react-dom.development.js:26466:5) at renderRootSync (react-dom.development.js:26434:7)
chartData数组的每个元素包含title、subtitle、images等字段。
问题原因
- Fisher-Yates算法实现错误:循环起始值设为
arr.length,但数组的最大有效索引是arr.length - 1,此时arr[i]为undefined,交换后数组中会混入undefined元素,导致map遍历访问data.title时出错。 - 直接修改原数组:React状态是不可变的,直接修改原数组会导致状态更新异常,且可能引发不可预测的渲染问题。
解决方案
1. 修正Fisher-Yates洗牌算法
调整循环起始值和随机索引范围,同时复制原数组避免修改原状态:
function shuffle(arr) { // 复制原数组,不直接修改状态中的原始数据 const newArr = [...arr]; // 从最后一个有效索引开始循环 for (let i = newArr.length - 1; i > 0; i--) { // 生成0到i(包含i)的随机索引 const j = Math.floor(Math.random() * (i + 1)); // 解构赋值交换元素 [newArr[i], newArr[j]] = [newArr[j], newArr[i]]; } return newArr; }
2. 正确更新状态
调用洗牌函数时,直接传入原数组即可(洗牌函数内部已做拷贝):
function playShuffle() { setChartData(shuffle(chartData)); }
3. 可选:添加空值保护
在Home组件的map遍历中增加空值判断,避免因数组元素异常导致报错:
const songTitleEl = chartData.map((data, i) => { if (!data) return null; // 跳过undefined元素 return ( <ChartSongs key={data.key} title={data.title} artiste={data.subtitle} coverarts={data.images?.coverart} index={i} /> ) })
修改后洗牌功能可正常运行,不会再出现undefined元素导致的报错。
内容的提问来源于stack exchange,提问作者Tomiwa
相关产品推荐
相关产品推荐

