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

React Router使用Link跳转返回/play路由时Play组件Firestore数据不渲染

核心问题拆解

你观察到的DOM空白、控制台可正常打印数据的现象,本质是两处代码写法错误叠加导致的,和React-Router的Link组件本身无直接关系:

  • 完全没有通过setState更新状态,直接修改了state原数组
    你在useState解构出users数组后,从来没有调用过对应的setUsers方法,只是在Firestore请求的回调里直接往原数组里push数据。这种操作属于React明确禁止的可变状态修改,完全不会触发组件重渲染。
    你能在控制台正常打印users数据,是因为闭包里拿到的数组引用确实被填充了内容,但React内部维护的状态更新机制完全感知不到这个变化,自然不会执行后续的渲染流程。
    首次加载页面时能正常显示,是因为刚好撞上了其他流程触发组件重渲染的时机(比如父组件状态更新、路由内置副作用),此时render函数读到了已经被你填充了数据的数组,才展示出正常内容。
    第二次回到/play路由时,组件重新挂载,首次渲染会输出加载spinner,之后异步请求回来,你在回调里往闭包的数组里push了数据——因为全程没有调用setUsers,React完全感知不到状态变化,根本不会重新执行render逻辑,这就是为什么连spinner分支都看不到:组件停在首次渲染后的状态,没有任何更新动作。
  • 使用随机数作为列表key,引发DOM渲染异常
    你在渲染用户列表时用Math.random()作为每个列表项的key,这种不稳定的key会导致React在diff对比虚拟DOM时,无法正确识别已有的DOM节点,极端情况下会把已渲染的内容直接清空。
为什么用a标签跳转就正常

普通a标签会触发整页刷新,整个React应用从头完成初始化挂载,所有状态完全重置,异步请求回来后绕开了你代码里的状态更新bug,自然能正常渲染。这不是a标签比Link功能更强,只是整页刷新的机制刚好掩盖了代码本身的问题。

修复方案
  1. 改掉可变状态的写法,在请求拿到数据后创建全新的数组引用,通过setUsers触发React重渲染:
    useEffect(() => {
      // 增加卸载标记,避免组件离开后还执行状态更新引发内存泄漏
      let isComponentMounted = true;
      db.collection("users").orderBy("points", "desc").limit(10).get()
        .then((data: any) => {
          if (!isComponentMounted) return;
          const userList: any[] = [];
          data.forEach((usr: any) => {
            userList.push(usr.data());
          })
          // 创建新数组,通过setState触发重渲染
          setUsers(userList);
        })
        .catch((err: any) => {
          console.log("An error has occured: ", err.message);
        })
      // 组件卸载时把标记设为false
      return () => {
        isComponentMounted = false;
      }
    }, [])
    
  2. 替换不稳定的随机数key,用用户的唯一标识(比如username、Firestore文档ID)作为列表项的key:
    users.map((d: any) => {
      return (
        <div className="userWrapper" key={d.username}>
          <Link to={`/user/${d.username}`}><img className="lbImage" src={d.profileImage} /></Link>
          <Link to={`/user/${d.username}`}>
            <p className="lbUser">{d.username} {d.pro ? <img src={Pro} className="lbPro" /> : null} 
            </p>
          </Link>
          <p className="lbPoints">{d.points} Points, {d.rank} ({d.races} Tests)</p>
        </div>
      )
    })
    

内容的提问来源于stack exchange,提问作者Tudor Alexandru

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 07:57:03