Next.js中React Context始终取默认值报setName非函数问题排查
错误原因
核心问题是违反React Context的基础使用规则:只有被Context.Provider包裹的后代组件,才能通过hook消费到Provider传入的上下文值,在Provider外层调用Context Hook只会拿到定义Context时设置的默认值。
你的代码里,usePeople()是在MyApp组件顶层调用的,但<PeopleProvider>是MyApp自身return时才渲染的父容器,MyApp本身完全处于Provider的作用域之外,所以拿到的setName、setAge永远是默认值里的null,调用时自然抛出setName is not a function错误。
另外你贴的代码里还有一处语法笔误:setName(name)后多写了一个多余的右括号,修复结构问题时记得一并删除。
修复方案
两种实现都可以正常运行,优先推荐第一种:
- 方案1:将初始化逻辑内聚到PeopleProvider内部
这是最合理的实现方式,Context的状态初始化逻辑本来就应该封装在Provider组件内,不需要放到_app层处理。直接修改PeopleProvider的state定义,把环境变量值作为state初始值即可:
// PeopleProvider 组件代码 const peopleContextDefaultValue = { name: null, age: null, setName: null, setAge: null, }; // ...其他逻辑 // 初始化state时直接读取环境变量作为默认值 const [name, setNameState] = useState(process.env.NEXT_PUBLIC_NAME || null); const [age, setAgeState] = useState(process.env.NEXT_PUBLIC_AGE ? Number(process.env.NEXT_PUBLIC_AGE) : null); const setName = (payload) => { setNameState(payload); }; const setAge = (payload) => { setAgeState(payload); }; const value = { name, age, setName, setAge, }; return ( <PeopleContext.Provider value={value}> {children} </PeopleContext.Provider> );
改完后_app.js里不需要再写那段useEffect逻辑,直接渲染Provider包裹路由组件即可:
function MyApp({ Component, pageProps }: AppProps) { return ( <PeopleProvider> <Navbar /> <Component {...pageProps} /> </PeopleProvider> ); } export default MyApp;
这种写法的优势是不需要等useEffect执行再赋值,首屏渲染时Context就已经持有正确的初始值,不会出现状态短暂为null的闪烁问题,代码逻辑也更内聚好维护。
- 方案2:抽离子组件承载初始化逻辑
如果因为特殊需求必须把初始化逻辑写在_app层,就不能在MyApp顶层直接调用usePeople,需要把消费Context、执行初始化的逻辑拆成独立子组件,放到PeopleProvider的内部渲染:
// 单独抽离初始化组件,作为Provider的后代节点可以正常消费Context function ContextInit() { const { setName, setAge } = usePeople(); useEffect(() => { const name = process.env.NEXT_PUBLIC_NAME; const age = process.env.NEXT_PUBLIC_AGE; if (name) { setName(name) } if (age) { setAge(age) } }, [setName, setAge]); return null; } function MyApp({ Component, pageProps }: AppProps) { return ( <PeopleProvider> <ContextInit /> <Navbar /> <Component {...pageProps} /> </PeopleProvider> ); } export default MyApp;
注意这种方案是在客户端useEffect触发后才给Context赋值,首屏首次渲染时Context的name、age还是null,如果页面组件首屏就依赖这两个值做渲染/逻辑判断,会出现短暂的空值状态,非必要不选这个方案。
内容的提问来源于stack exchange,提问作者ethdude12
相关产品推荐
相关产品推荐

