如何让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等待上下文变量加载完成再请求
做两个关键调整即可解决:
将用户数据加入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]);过滤初始空状态
初始时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
相关产品推荐
相关产品推荐

