如何修复cannot read properties of null (reading 'useContext')及无效Hook调用报错?
React Hook调用错误与代码修复
问题与错误信息
无法读取null的属性'useContext' && react.development.js:209 警告:无效的Hook调用。Hooks只能在函数组件的主体内部调用。可能的原因如下:
- React和渲染器(比如React DOM)版本不匹配
- 违反了Hooks的规则
- 同一个应用中有多个React副本
现有代码文件
App.js
function App() { return ( <React.Fragment> <Counter/> </React.Fragment> ); } export default App;
index.js
const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider context={StoreContext} store={Store()}> <App /> </Provider> ); reportWebVitals();
CounterReducer.js
const CounterReducer = (state = { count: 0 } , action) => { switch (action.type) { case handleDencrement: return state.count - 1 case handleIncrement: return state.count + 1 default: return state } } export default CounterReducer;
context.js
const StoreContext = React.createContext(); export default StoreContext ;
Store.js
const Store = () => { const store = useStore(CounterReducer); return store } export default Store;
types.js
export const handleIncrement = 'handleIncrement' ; export const handleDencrement = 'handleDencrement';
Counter.js
const Counter = () => { const [count, setcount] = useState(0); const handleIncrement = () => { setcount(count + 1); } const handleDencrement = () => { setcount(count - 1); } return ( <div> <center> <h1>Redux</h1> <h1>{count}</h1> <button className="btn btn-primary" onClick={handleIncrement}>Increment</button> <button className="btn btn-warning" onClick={handleDencrement}>decrement</button> </center> </div> ); } export default Counter;
错误根源与修复方案
1. 违反Hook调用规则
Store.js中的Store函数包含Hook调用,绝对不能在组件外部直接执行。你在index.js里直接写store={Store()},相当于在React组件渲染流程之外调用了Hook(useStore),直接触发了无效Hook调用的警告。
2. Context Provider使用错误
你用了<Provider>,但React Context的Provider应该是StoreContext.Provider,并且通过value属性传递数据,而不是context和store属性。
3. Reducer返回值错误
CounterReducer里的返回值是数字(比如state.count -1),这会把原本的对象状态{count:0}覆盖成数字,后续再访问state.count就会报错(数字没有count属性),必须返回完整的状态对象。
4. 未正确关联状态与组件
Counter组件目前用的是自己的useState,完全没用到你定义的Context/Reducer,等于状态管理逻辑和组件脱节。
完整修复后的代码示例
修复Store.js(改成自定义Hook)
import { useReducer } from 'react'; import CounterReducer from './CounterReducer'; export const useStore = () => { const [state, dispatch] = useReducer(CounterReducer, { count: 0 }); return { state, dispatch }; }
修复index.js(正确使用Context Provider)
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import StoreContext from './context'; import { useStore } from './Store'; // 用组件包裹,在内部调用Hook const RootWrapper = () => { const store = useStore(); return ( <StoreContext.Provider value={store}> <App /> </StoreContext.Provider> ); }; const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<RootWrapper />); reportWebVitals();
修复CounterReducer.js(返回完整状态对象)
import { handleIncrement, handleDencrement } from './types'; const CounterReducer = (state = { count: 0 }, action) => { switch (action.type) { case handleDencrement: return { ...state, count: state.count - 1 }; case handleIncrement: return { ...state, count: state.count + 1 }; default: return state; } }; export default CounterReducer;
修复Counter.js(使用Context获取状态和dispatch)
import { useContext } from 'react'; import StoreContext from './context'; import { handleIncrement, handleDencrement } from './types'; const Counter = () => { const { state, dispatch } = useContext(StoreContext); return ( <div> <center> <h1>Redux风格状态管理</h1> <h1>{state.count}</h1> <button className="btn btn-primary" onClick={() => dispatch({ type: handleIncrement })}>Increment</button> <button className="btn btn-warning" onClick={() => dispatch({ type: handleDencrement })}>decrement</button> </center> </div> ); }; export default Counter;
内容的提问来源于stack exchange,提问作者Djamel Eddine
相关产品推荐
相关产品推荐

