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

如何让useEffect等待UserContext加载完成后再发起API请求?

问题描述

通过UserContext加载用户数据,在useEffect钩子中使用该数据发起API请求,但数据加载延迟导致请求中出现未定义变量。请问如何让useEffect等待上下文变量加载完成后再发送请求?同时想了解如何确保在整个应用中发起请求前用户数据已加载?

相关代码

UserContext.js

import { createContext, useState } from "react";

const UserContext = createContext({});

export const UserProvider = ({ children }) => {
  const [user, setUser] = useState({});

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

export default UserContext;

自定义钩子 useUser.js

import { useContext } from "react";
import UserContext from "../context/UserProvider";

const useUser = () => {
  return useContext(UserContext);
};

export default useUser;

个人页面中的API调用

const { user } = useUser();

useEffect(() => {
  Axios.get(
    `API_URL/${user?.subscription_id}`
  ).then((res) => {
    console.log(res)
  });
}, []);
解决方案

一、让useEffect等待上下文变量加载完成再请求

做两个关键调整即可解决:

  1. 将用户数据加入useEffect依赖数组
    当用户数据更新时,useEffect会重新执行。同时通过条件判断过滤无效状态,避免发送错误请求:

    const { user } = useUser();
    
    useEffect(() => {
      // 仅当subscription_id存在时发起请求
      if (user?.subscription_id) {
        Axios.get(`API_URL/${user.subscription_id}`)
          .then((res) => {
            console.log(res);
          });
      }
    }, [user]); // 依赖user对象,数据更新时触发钩子
    

    也可以更精准地依赖user.subscription_id,减少不必要的钩子执行:

    useEffect(() => {
      if (user.subscription_id) {
        Axios.get(`API_URL/${user.subscription_id}`)
          .then((res) => {
            console.log(res);
          });
      }
    }, [user.subscription_id]);
    
  2. 过滤初始空状态
    初始时user是空对象,user?.subscription_id返回undefined,通过if条件跳过这种情况,确保只有有效数据存在时才发送请求。

二、确保整个应用发起请求前用户数据已加载

可以通过以下几种全局方案实现:

1. 在UserProvider中预加载用户数据

修改UserProvider,在渲染子组件前先完成用户数据加载,同时维护加载状态:

import { createContext, useState, useEffect } from "react";
import Axios from "axios";

const UserContext = createContext({});

export const UserProvider = ({ children }) => {
  const [user, setUser] = useState({});
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    // 调用获取用户信息的API
    Axios.get("/api/user")
      .then((res) => {
        setUser(res.data);
      })
      .finally(() => {
        setIsLoading(false);
      });
  }, []);

  // 加载中时显示加载态,避免渲染依赖用户数据的组件
  if (isLoading) {
    return <div>加载中...</div>; // 可替换为自定义加载组件
  }

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

export default UserContext;

所有子组件渲染时,用户数据已加载完成,不会出现未定义问题。

2. 使用路由守卫(React Router)

通过路由层面的守卫,限制只有用户数据加载完成后才能访问相关页面:

import { Navigate, Outlet } from "react-router-dom";
import useUser from "./hooks/useUser";

const RequireAuth = () => {
  const { user } = useUser();

  // 判断用户数据是否有效
  if (!user?.subscription_id) {
    return <Navigate to="/login" replace />; // 或显示加载态
  }

  return <Outlet />;
};

在路由配置中,将需要用户数据的页面包裹在RequireAuth中:

<Route element={<RequireAuth />}>
  <Route path="/profile" element={<ProfilePage />} />
  {/* 其他依赖用户数据的页面 */}
</Route>

3. 全局请求拦截器

给Axios添加请求拦截器,每次请求前检查用户数据是否存在,不存在则等待加载完成:

import Axios from "axios";
import useUser from "./hooks/useUser";

// 全局维护用户加载完成的Promise
let userLoadedPromise = null;

const ensureUserLoaded = () => {
  if (!userLoadedPromise) {
    userLoadedPromise = new Promise((resolve) => {
      const checkUser = () => {
        const { user } = useUser();
        if (user?.subscription_id) {
          resolve(user);
        } else {
          setTimeout(checkUser, 100);
        }
      };
      checkUser();
    });
  }
  return userLoadedPromise;
};

// 添加请求拦截器
Axios.interceptors.request.use(async (config) => {
  await ensureUserLoaded();
  return config;
});

这种方式适合全局需要确保用户数据存在的场景,注意避免内存泄漏问题。

内容的提问来源于stack exchange,提问作者Husnain Mehmood

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 17:36:11