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

Next.js页面快速切换cid时渲染错误数据问题求助

问题:Next.js页面切换查询参数时渲染错误数据

页面基于查询参数cid展示客户信息,通过Axios从API获取数据。快速切换不同cid(如1→2→1)时,页面偶尔显示错误的cid对应数据,比如切回cid=1后仍显示cid=2的内容。

原因分析

核心问题是异步请求竞态:

  • 快速切换cid时,前一个cid的异步请求尚未完成,新请求已发起。若旧请求晚于新请求完成,会用旧数据覆盖新状态,导致渲染错误。
  • 当前代码中AbortController的使用错误:在finally里直接调用controller.abort(),会在请求完成后立刻中止,无法起到取消未完成请求的作用。

解决方案

1. 正确使用AbortController取消旧请求

在useEffect中创建控制器,当cid变化时,先取消上一次未完成的请求,再发起新请求:

useEffect(() => {
  const controller = new AbortController();
  const { signal } = controller;

  const fetchData = async () => {
    try {
      if (router.query.cid) {
        // 并行请求优化,同时获取两个数据
        const [customerData, groupsData] = await Promise.all([
          fetchCustomer(router.query.cid, signal),
          fetchGroups(router.query.cid, signal)
        ]);
        setCustomer(customerData);
        setGroups(groupsData);
      }
    } catch (error) {
      // 忽略取消请求的错误
      if (error.name !== 'AbortError') {
        console.error('Error fetching customer or groups:', error);
      }
    }
  };

  fetchData();

  // 组件卸载或cid变化时,取消未完成的请求
  return () => controller.abort();
}, [router.query.cid]);

// 修改fetch函数,接收signal参数
const fetchCustomer = async (cid, signal) => {
  try {
    const res = await axios.get(`/api/customer?cid=${cid}`, { signal, ...config });
    return res.data[0];
  } catch (err) {
    console.error(err);
    throw err;
  }
};

const fetchGroups = async (cid, signal) => {
  try {
    const res = await axios.get(`/api/customer/groups?cid=${cid}`, { signal, ...config });
    return res.data;
  } catch (error) {
    console.error(error);
    throw error;
  }
};

2. 添加请求结果校验(双重保险)

即使请求未被取消,更新状态前校验当前cid是否与请求时一致:

useEffect(() => {
  const currentCid = router.query.cid; // 保存请求时的cid

  const fetchData = async () => {
    try {
      if (currentCid) {
        const customerData = await fetchCustomer(currentCid);
        const groupsData = await fetchGroups(currentCid);
        // 校验当前cid是否未变化,再更新状态
        if (router.query.cid === currentCid) {
          setCustomer(customerData);
          setGroups(groupsData);
        }
      }
    } catch (error) {
      if (error.name !== 'AbortError') {
        console.error('Error fetching customer or groups:', error);
      }
    }
  };

  fetchData();
}, [router.query.cid]);

3. 优化加载状态(可选)

添加加载状态,避免切换时显示旧数据:

const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
  const controller = new AbortController();
  const { signal } = controller;
  const currentCid = router.query.cid;

  const fetchData = async () => {
    if (!currentCid) return;
    setIsLoading(true);
    try {
      const [customerData, groupsData] = await Promise.all([
        fetchCustomer(currentCid, signal),
        fetchGroups(currentCid, signal)
      ]);
      if (router.query.cid === currentCid) {
        setCustomer(customerData);
        setGroups(groupsData);
      }
    } catch (error) {
      if (error.name !== 'AbortError') {
        console.error('Error fetching customer or groups:', error);
      }
    } finally {
      setIsLoading(false);
    }
  };

  fetchData();
  return () => controller.abort();
}, [router.query.cid]);

// 渲染时优先显示加载状态
if (customers.length < 1 || isLoading) {
  return <Spinner />;
}

内容的提问来源于stack exchange,提问作者Brandon Han

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:52:38