为何App组件无法获取React Context的更新值?
问题:Context更新无法传递到App组件
以下是我尝试通过自定义Hook配置Context的代码,以及遇到的问题:
代码片段
usePlayerInterface.jsx
import { useState } from 'react'; export function usePlayerInterface() { const [userAction, setUserAction] = useState(false); // ...code that calls setUserAction from a user generated event }
PlayerInterfaceContext.jsx
import { createContext } from 'react'; const PlayerInterfaceContext = createContext({ userAction: false }); export default PlayerInterfaceContext;
StandardExerciseContainer.jsx
import { usePlayerInterface } from '../hooks/usePlayerInterface'; import { useEffect } from 'react'; import PlayerInterfaceContext from '../context/PlayerInterfaceContext'; const StandardExerciseContainer = ({ children }) => { const { userAction } = usePlayerInterface(); useEffect(() => { // THIS GETS CALLED AS EXPECTED console.log('userAction in container:', userAction); }, [userAction]); return ( <PlayerInterfaceContext.Provider value={{ userAction }}> {children} </PlayerInterfaceContext.Provider> ) } export default StandardExerciseContainer;
App.jsx
import { useEffect, useContext } from 'react'; import StandardExerciseContainer from '@common/react/components/StandardExerciseContainer.jsx'; import PlayerInterfaceContext from '../context/PlayerInterfaceContext'; const App = () => { const { userAction } = useContext(PlayerInterfaceContext); useEffect(() => { // THIS IS ONLY CALLED FOR THE INITIAL VALUE console.log('userAction in app:', userAction); }, [userAction]); return ( <StandardExerciseContainer> // ...child components </StandardExerciseContainer> ) }
日志输出
on page load: userAction in container: false userAction in app: false on user event userAction in container: true
App组件始终无法获取更新后的userAction值,找不到问题所在。
问题原因
核心问题是App组件不在PlayerInterfaceContext.Provider的包裹范围内。
StandardExerciseContainer内部渲染了Provider,而App是渲染StandardExerciseContainer的父组件,App调用useContext时,只能拿到Context的默认值,无法感知Provider内部的状态更新——因为Context的作用域是Provider的子组件树,父组件不在这个范围内。
解决方案
调整组件层级,让需要接收Context更新的部分处于Provider的包裹之下,有两种常见实现方式:
方式一:将Provider提升到App外层
修改项目入口文件(比如index.jsx),把Provider放在App组件外面:
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import PlayerInterfaceContext from './context/PlayerInterfaceContext'; import { usePlayerInterface } from './hooks/usePlayerInterface'; const Root = () => { const { userAction } = usePlayerInterface(); return ( <PlayerInterfaceContext.Provider value={{ userAction }}> <App /> </PlayerInterfaceContext.Provider> ); }; ReactDOM.createRoot(document.getElementById('root')).render(<Root />);
同时修改StandardExerciseContainer,移除内部的Provider:
import { usePlayerInterface } from '../hooks/usePlayerInterface'; import { useEffect } from 'react'; const StandardExerciseContainer = ({ children }) => { const { userAction } = usePlayerInterface(); useEffect(() => { console.log('userAction in container:', userAction); }, [userAction]); return <>{children}</>; }; export default StandardExerciseContainer;
方式二:抽离App的业务逻辑到子组件
把App中需要使用Context的部分抽成独立组件,让它成为StandardExerciseContainer(即Provider)的子元素:
import { useEffect, useContext } from 'react'; import StandardExerciseContainer from '@common/react/components/StandardExerciseContainer.jsx'; import PlayerInterfaceContext from '../context/PlayerInterfaceContext'; // 抽离需要使用Context的业务逻辑 const AppContent = () => { const { userAction } = useContext(PlayerInterfaceContext); useEffect(() => { console.log('userAction in app:', userAction); }, [userAction]); return ( // ...child components ); }; const App = () => { return ( <StandardExerciseContainer> <AppContent /> </StandardExerciseContainer> ); }; export default App;
这样AppContent处于Provider的作用域内,就能正常接收Context的状态更新了。
内容的提问来源于stack exchange,提问作者Rooster242
相关产品推荐
相关产品推荐

