React渲染对象数组元素出现屏幕闪烁后消失问题求助
问题根因
- 异步请求处理逻辑错误:你在
getPlayerIDs函数中发起的所有axios请求都是异步执行的,你在发起请求后立即执行setListOfStats(playerList),此时所有接口请求还未返回结果,playerList是空数组。后续接口请求返回后往playerList中push数据属于数组原地修改,没有触发React的状态更新,所以页面无法渲染最新数据。偶尔闪烁是极个别请求在setListOfStats执行前刚好返回的偶发现象。 - 逻辑判断冗余错误:在门将位置(playerPos=1)的分支中,你写了
if(playerPos !== 1 )的判断,导致所有门将数据永远不会被推入playerList数组。 - 状态设计冗余:
triggerStats状态完全没有存在必要,直接通过listOfStats数组长度判断是否有数据即可。 - 组件复用逻辑缺失:切换球员位置时LeaderStats组件会复用旧实例,不会重新触发数据请求,导致位置和数据不匹配。
修复方案
步骤1:修改异步请求聚合逻辑,用Promise.all等待所有请求完成后再更新状态
步骤2:删除冗余的if(playerPos !== 1 )判断
步骤3:移除不必要的triggerStats状态和关联useEffect
步骤4:将axios导入放到组件顶部,不要在函数内部require
步骤5:给LeaderStats绑定位置相关的key,保证切换位置时重新初始化组件
修复后的核心代码
LeaderStats组件完整代码
import { useEffect, useState, useRef } from "react"; import axios from 'axios'; import '../CSS/LeaderStats.css'; function LeaderStats({playerPos, teams}){ const isInitialMount = useRef(true); const [listOfStats, setListOfStats] = useState([]); //teams和playerPos变更时都需要重新获取数据 useEffect(()=>{ if (isInitialMount.current) { isInitialMount.current = false; }else{ if(teams.length !== 0){ getPlayerIDs(teams); }else{ setListOfStats([]); } } },[teams, playerPos]) async function getPlayerIDs(teamID){ const requestList = []; // 先收集所有球队的花名册请求,等待全部返回 const teamRosterRequests = teamID.map(d => axios.get(`https://statsapi.web.nhl.com/api/v1/teams/${parseInt(d)}/roster`)); const rosterResponses = await Promise.all(teamRosterRequests); // 遍历所有花名册,收集所有符合位置要求的球员数据请求 rosterResponses.forEach(res => { let positionFilter; if(playerPos === 0) positionFilter = 'Forward'; else if(playerPos === 1) positionFilter = 'Goalie'; else positionFilter = 'Defenseman'; const targetPlayers = res.data.roster.filter(obj => obj.position.type === positionFilter); targetPlayers.forEach(e => { requestList.push( axios.get(`https://statsapi.web.nhl.com/api/v1/people/${e.person.id}/stats?stats=statsSingleSeason&season=20212022`) .then(res => { if(typeof res.data.stats[0].splits[0] === 'undefined') return null; const stat = res.data.stats[0].splits[0].stat; if(playerPos === 1){ return { ID: `${e.person.id}`, Name: `${e.person.fullName}`, GAA: `${stat.goalAgainstAverage}`, SavePercentage: `${stat.savePercentage}`, Shutouts: `${stat.shutouts}` }; }else{ return { ID: `${e.person.id}`, Name: `${e.person.fullName}` , Points: `${stat.points}`, Goals: `${stat.goals}`, Assists: `${stat.assists}` }; } }) ) }) }) // 等待所有球员请求完成,过滤无效空值后更新状态 const allStats = await Promise.all(requestList); setListOfStats(allStats.filter(item => item !== null)); } return( <div className = 'Player-Stats'> {listOfStats.length > 0 ? listOfStats.map((d)=> <p className = 'Stats' key={d.ID}>{d.ID} - {d.Name}</p>) : <p className = 'Stats'> 暂无可用数据</p>} </div> ) } export default LeaderStats;
LeagueLeaders组件调用修改
{/* 添加key属性保证切换位置时组件重新初始化 */} <LeaderStats key={titleNo} playerPos = {titleNo} teams = {getListTeams}/>
TeamSelector组件优化建议
不要直接操作DOM修改logo的opacity,用selectedTeams数组判断对应ID是否选中来绑定样式,避免DOM操作和状态不同步的问题:
<img onClick={(e) => handleClick(e)} key = {Teams.ID} id = {Teams.ID} alt = {Teams.Name +' Logo'} src = {Teams.URL} className = 'logo' style={{opacity: selectedTeams.includes(Teams.ID) ? '100%' : '25%'}} />
内容的提问来源于stack exchange,提问作者CanadaBound
相关产品推荐
相关产品推荐

