React useContext状态更新未触发useEffect问题求助
兄弟,我一眼就看到你代码里最核心的问题了——你把LicenseContextProvider放错位置啦! 这直接导致你的LandingPage根本没在Provider的上下文范围内,所以状态更新完全传不到组件里,自然也触发不了useEffect。
问题根源分析
你现在的LandingPage组件逻辑是:先调用useLicense()获取上下文,然后在自己的JSX里渲染LicenseContextProvider。但React组件的执行顺序是先跑组件逻辑(包括useLicense()),再渲染JSX。这就意味着:
- 当
useLicense()执行时,LicenseContextProvider还没被渲染,所以useContext拿到的是你初始化createContext时的默认空对象(你用了{} as LicenseContextModel强制类型断言,所以是空对象)。 - 后面
LicenseContextProvider内部的状态更新,只会影响它包裹的子组件,但LandingPage本身不在这个Provider的范围内,所以状态更新和它完全没关系。
看你控制台的日志也能佐证这一点:一开始context是空对象,page isLoading是undefined,而Provider内部的licenseContext明明已经更新了,但就是传不到LandingPage里。
修复步骤
1. 把Provider移到全局或父组件(Next.js推荐在_app.tsx配置)
因为你用的是Next.js,最合理的做法是在全局的_app.tsx里用LicenseContextProvider包裹整个应用,这样所有页面都能共享上下文:
// _app.tsx import type { AppProps } from 'next/app' import LicenseContextProvider from '@/contexts/license-context' export default function App({ Component, pageProps }: AppProps) { return ( <LicenseContextProvider> <Component {...pageProps} /> </LicenseContextProvider> ) }
2. 修改LandingPage,移除内部的Provider
现在LandingPage已经在全局Provider的范围内了,直接用useLicense()就行,不需要自己嵌套Provider:
// LandingPage.tsx import React, { useEffect } from "react"; import { useLicense } from '@/contexts/license-context'; import { useRouter } from 'next/navigation'; const LandingPage = () => { const { product, api, isLoading } = useLicense(); const router = useRouter(); useEffect(() => { console.log('page isLoading', isLoading); if (!isLoading) { console.log('product', product); console.log('api', api); if (!product) { router.push('/license-warning'); } else if (!api) { // 这里改成!api更贴合你的初始值逻辑 router.push('/key-manager'); } } },[product, api, isLoading]); return ( <> <title>Landing Page</title> <div className="w-full"> <div className="flex flex-col w-full"> <p>Landed on the page</p> </div> </div> </> ); }; export default LandingPage;
3. 优化上下文初始化(可选但推荐)
你现在用{} as LicenseContextModel强制断言默认上下文,类型不够安全。可以改成:
// license-context.tsx export const LicenseContext = createContext<LicenseContextModel | undefined>(undefined); export function useLicense(): LicenseContextModel { const context = useContext(LicenseContext); if (!context) { throw new Error('useLicense must be used within a LicenseContextProvider'); } return context; }
这样如果不小心在Provider外调用useLicense(),会直接抛出明确的错误,更容易排查问题。
修复后效果
修改完成后,LandingPage会正确获取Provider的上下文状态,当Provider里的isLoading、product、api更新时,组件的useEffect会被正确触发,路由跳转逻辑也能正常工作。
备注:内容来源于stack exchange,提问作者JediPotPie

