You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React渲染对象数组元素出现屏幕闪烁后消失问题求助

问题根因

  1. 异步请求处理逻辑错误:你在getPlayerIDs函数中发起的所有axios请求都是异步执行的,你在发起请求后立即执行setListOfStats(playerList),此时所有接口请求还未返回结果,playerList是空数组。后续接口请求返回后往playerList中push数据属于数组原地修改,没有触发React的状态更新,所以页面无法渲染最新数据。偶尔闪烁是极个别请求在setListOfStats执行前刚好返回的偶发现象。
  2. 逻辑判断冗余错误:在门将位置(playerPos=1)的分支中,你写了if(playerPos !== 1 )的判断,导致所有门将数据永远不会被推入playerList数组。
  3. 状态设计冗余:triggerStats状态完全没有存在必要,直接通过listOfStats数组长度判断是否有数据即可。
  4. 组件复用逻辑缺失:切换球员位置时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 08:15:04