如何在React-Router-6的loader中访问全局Context的setIsLoading?
解决方案:React Router 6 Loader 中修改全局加载状态
由于 React Router 的 loader 函数运行在组件渲染上下文之外,无法直接调用 React Hooks(包括 useContext),因此可以通过以下两种方案解决:
方案一:利用 React Router 内置导航状态(推荐)
React Router 6 提供的 useNavigation 钩子可以直接获取路由加载状态,无需自行维护全局 Context:
- 创建全局加载组件
// components/GlobalLoading.jsx import { useNavigation } from "react-router-dom"; export default function GlobalLoading() { const navigation = useNavigation(); const isLoading = navigation.state === "loading"; return isLoading ? ( <div style={{ position: "fixed", top: 0, left: 0, right: 0, bottom: 0, background: "rgba(0,0,0,0.5)", display: "flex", alignItems: "center", justifyContent: "center", color: "white" }}> 加载中... </div> ) : null; }
- 在布局组件中引入
// 修改 AppLayout const AppLayout = () => ( <> <GlobalLoading /> <MenuBar /> <Outlet /> </> );
这个方案无需修改现有 loader 和 Context,直接复用 React Router 内置的加载状态,覆盖所有路由 loader 的加载场景。
方案二:独立状态管理模块(自定义加载逻辑)
如果需要自定义加载逻辑(比如同时管理 loader 之外的异步请求),可以创建独立的状态管理模块,脱离 React Context 限制:
- 创建加载状态管理文件
// utils/loadingState.js let isLoading = false; const listeners = []; export const setLoading = (newState) => { isLoading = newState; listeners.forEach(listener => listener(isLoading)); }; export const getLoading = () => isLoading; export const subscribeLoading = (listener) => { listeners.push(listener); // 返回取消订阅函数 return () => { listeners = listeners.filter(l => l !== listener); }; };
- 集成到 GlobalContext
// 修改 GlobalContext.jsx import { createContext, useContext, useEffect, useState, ReactNode, } from "react"; import { getLoading, setLoading, subscribeLoading } from "../utils/loadingState"; interface GlobalContextValue { isLoading: boolean; setIsLoading: (newState: boolean) => void; } const GlobalContext = createContext<GlobalContextValue>({ isLoading: false, setIsLoading: () => {}, }); export const useGlobalContext = () => useContext(GlobalContext); export const GlobalProvider: React.FC<{ children: ReactNode }> = ({ children }) => { const [localIsLoading, setLocalIsLoading] = useState(getLoading()); useEffect(() => { // 订阅状态变化,同步到组件状态 const unsubscribe = subscribeLoading(newState => { setLocalIsLoading(newState); }); return unsubscribe; }, []); const handleSetIsLoading = (newState) => { setLoading(newState); }; return ( <GlobalContext.Provider value={{ isLoading: localIsLoading, setIsLoading: handleSetIsLoading, }} > {children} </GlobalContext.Provider> ); };
- 在 loader 中调用状态修改
// 修改 profileLoader import { setLoading } from "../utils/loadingState"; const profileLoader = async () => { setLoading(true); try { const auth = getAuth(); const user = auth.currentUser; const result = await getUserById(user?.email); return result || null; } finally { // 无论请求成功失败都关闭加载状态 setLoading(false); } };
这个方案允许 loader 和组件共享同一加载状态,同时支持自定义扩展。
内容的提问来源于stack exchange,提问作者Borislav Stefanov
相关产品推荐
相关产品推荐

