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

Next.js中router.query获取的id刷新页面时返回undefined如何解决?

Next.js 刷新页面时router.query参数报错解决方案

问题原因

这个报错是Next.js Pages Router的路由加载机制导致的:

  • 通过next/link触发客户端跳转时,路由上下文已经完成初始化,router.query可以直接拿到完整的roomId参数,因此功能运行正常
  • 手动刷新页面时,首次渲染(包含服务端直出阶段、客户端hydration注水阶段)router.query初始值为空对象,此时解构得到的roomId为undefined,代码中直接访问rooms[roomId].name等价于读取undefined的属性,直接触发运行时错误。

刷新页面抛出的运行时错误截图

原业务代码所有依赖roomId的渲染逻辑都没有做参数就绪校验,是触发报错的直接原因。

注:如果你使用Next.js 13+的App Router,直接通过useParams()钩子获取路由参数不会出现首屏空值问题,无需额外做加载判断。

可行解决方案

方案1:增加路由就绪判断(通用兼容方案)

利用router.isReady属性判断路由参数是否解析完成,未就绪时渲染加载占位,等参数拿到后再渲染业务内容,同时增加房间不存在的兜底逻辑,代码示例如下:

import { useRouter } from 'next/router'
import styles from '@/styles/Room.module.css'
// 其余组件、工具函数引入省略

export default function RoomPage() {
  const router = useRouter()
  const { roomId } = router.query

  // 路由未完成初始化时返回加载态
  if (!router.isReady) return <div className={styles.loading}>加载中...</div>

  // 提前取出当前房间数据,避免重复写rooms[roomId],同时做存在性校验
  const currentRoom = rooms[roomId]
  if (!currentRoom) return <div className={styles.error}>房间不存在或已被解散</div>

  // 后续业务渲染逻辑,所有原rooms[roomId]替换为currentRoom
  return (
    <>
      <div className={styles.header}>
        <div className={styles.title}>{currentRoom.name}</div>
        <div className={styles.headerCon}>
          <div className={styles.roomUsers}>
            <AvatarGroup
              max={3}
              total={Object.keys(currentRoom.users).length}
            >
              {Object.keys(currentRoom.users).map((userId) => (
                <Avatar
                  key={userId}
                  alt={users[userId].name}
                  src={users[userId].profile_pic}
                />
              ))}
            </AvatarGroup>
          </div>

          <div className={styles.headerIcons}>
            <div className={styles.react}>
              <AddReactionOutlinedIcon />
            </div>
            <div className={styles.notif}>
              <NotificationsNoneOutlinedIcon />
            </div>
            <div className={styles.more}>
              <MoreHorizOutlined />
            </div>
          </div>
        </div>
      </div>
      {user.name === currentRoom.createdBy.name ? (
        <div className={styles.begin}>
          {!roomActive ? (
            <button onClick={handleStartSession}>Start</button>
          ) : (
            <button onClick={handleEndSession}>End</button>
          )}
        </div>
      ) : null}
      <div className={styles.participators}>
        <div className={styles.pAvatars}>
          <Avatar
            alt={currentRoom.createdBy.name}
            src={currentRoom.createdBy.profile_pic}
          />
          <div className={styles.userName}>{currentRoom.createdBy.name}</div>
          <div className={styles.userRole}>Admin</div>
          <div>
            <audio autoPlay ref={userAudio} />
            {peers.map((peer, index) => {
              return <Audio key={index} peer={peer} />;
            })}
          </div>
        </div>
        {Object.keys(currentRoom.users)
          .filter((userId) => users[userId].name !== currentRoom.createdBy.name)
          .map((userId) => (
            <div className={styles.pAvatars} key={userId}>
              <Avatar alt={users[userId].name} src={users[userId].profile_pic} />
              <div className={styles.userName}>{users[userId].name}</div>
              <div className={styles.userRole}>Member</div>
            </div>
          ))}
      </div>
    </>
  );
}

额外修正:原代码用Math.random()生成列表key会导致每次重渲染都重新挂载组件,引发头像闪烁、音频流重连等问题,上面示例已经改为用唯一的用户ID作为key,符合React列表渲染规范。

方案2:通过getServerSideProps提前注入参数(适合SEO场景)

如果使用Pages Router,可以在服务端侧逻辑中直接解析路由参数,将roomId甚至提前拉取好的房间数据作为props传给页面组件,首屏渲染时就能拿到完整参数,不需要判断isReady:

// 页面组件内新增服务端逻辑
export async function getServerSideProps(context) {
  const { roomId } = context.params
  // 可在此处直接请求接口拉取房间详情、用户列表数据,一并传给组件
  return {
    props: {
      roomId
    }
  }
}

这种方案首屏没有加载态闪烁,对搜索引擎爬虫更友好,适合公开房间类页面。

方案3:可选链临时兜底(不推荐)

如果不想增加独立加载态,也可以通过可选链操作符+空值兜底暂时避免报错,但这种方式会出现页面内容从“加载中”闪到正常内容的体验问题:

// 所有访问嵌套属性的位置增加可选链
<div className={styles.title}>{rooms?.[roomId]?.name ?? '加载中...'}</div>

内容的提问来源于stack exchange,提问作者Jeoffrey Duke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 13:51:28