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

从next/router与localStorage获取用户ID的异步加载问题求助

问题描述

我正在开发一个社交媒体项目,包含posts.js和[id].js两个页面:

  • 在posts.js中,点击指定div会跳转到用户个人页面,代码如下:
<div
  className={styles.userDetails}
  onClick={() => { 
    router.push({ pathname : `/user/${props.user._id}`})
  }}
>
</div>
  • 在[id].js页面中,userId需要通过两种方式获取:
    • 从其他用户页面跳转过来时,从router.query.id中获取
    • 用户查看自身个人资料时,从localStorage中获取(用户登录时ID已存入localStorage)

我编写了以下代码,但页面加载时router.query.id会短暂处于undefined状态,导致userId为undefined,进而引发接口调用失败:

const [userId, setUserId] = useState()

const getUserData = async () => {
  try {
    if (router.query.id != undefined ) {
      setUserId(router.query.id)
    } else {
      setUserId(getUser())
    }
    const res = await instance.get(`/user/${userId}`)
    setUser(res.data)
  } catch(e) {
    console.log(e)
  }

useEffect(() => {
  getUserData()
  getPosts()
}, [router.query])
解决方案

方案1:同步获取userId后再发起请求

不要依赖useState存储的userId,直接在函数内先计算出当前有效的userId,再调用接口,避免异步更新导致的延迟问题:

const getUserData = async () => {
  try {
    // 优先取路由参数,没有则从localStorage获取
    const currentUserId = router.query.id ?? getUser();
    // 处理userId为空的极端情况(比如未登录访问自身资料)
    if (!currentUserId) {
      router.push('/login');
      return;
    }
    // 用同步获取到的userId调用接口
    const res = await instance.get(`/user/${currentUserId}`);
    setUser(res.data);
    // 可选:如果需要在state中保存userId,再更新状态
    setUserId(currentUserId);
  } catch(e) {
    console.log(e);
  }
};

useEffect(() => {
  getUserData();
  getPosts();
}, [router.query]);

方案2:拆分逻辑,确保参数存在再执行

将getUserData改为接收targetUserId参数,在useEffect中先判断路由状态,再决定调用方式:

const [userId, setUserId] = useState();

const getUserData = async (targetUserId) => {
  try {
    const res = await instance.get(`/user/${targetUserId}`);
    setUser(res.data);
  } catch(e) {
    console.log(e);
  }
};

useEffect(() => {
  let targetUserId;
  // 查看他人资料:路由参数存在时直接使用
  if (router.query.id) {
    targetUserId = router.query.id;
    setUserId(targetUserId);
    getUserData(targetUserId);
  } else {
    // 查看自身资料:从localStorage获取
    targetUserId = getUser();
    if (targetUserId) {
      setUserId(targetUserId);
      getUserData(targetUserId);
    } else {
      // 未登录状态跳转登录页
      router.push('/login');
    }
  }
  getPosts();
}, [router.query]);

方案3:等待路由完全就绪后执行(Next.js专属)

如果使用Next.js,可以利用router.isReady或路由事件,确保路由参数完全加载后再执行逻辑:

import { useRouter } from 'next/router';

const router = useRouter();

const getUserData = async () => {
  try {
    const currentUserId = router.query.id ?? getUser();
    if (!currentUserId) {
      router.push('/login');
      return;
    }
    const res = await instance.get(`/user/${currentUserId}`);
    setUser(res.data);
    setUserId(currentUserId);
  } catch(e) {
    console.log(e);
  }
};

useEffect(() => {
  // 路由已就绪直接执行
  if (router.isReady) {
    getUserData();
    getPosts();
  } else {
    // 监听路由加载完成事件
    const handleRouteComplete = () => {
      getUserData();
      getPosts();
      router.events.off('routeChangeComplete', handleRouteComplete);
    };
    router.events.on('routeChangeComplete', handleRouteComplete);
    // 组件卸载时移除监听
    return () => router.events.off('routeChangeComplete', handleRouteComplete);
  }
}, [router.isReady, router.query]);

内容的提问来源于stack exchange,提问作者Unnikrishnan KJ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 14:35:24