React简易计数器应用报错:dispatch is not a function
React Context + useReducer 报错'dispatch is not a function'且counter初始值不显示的解决方法
问题根源
你在CounterApp组件的顶层直接调用useCounter()和useCounterDispatch(),但这两个Hook依赖的CounterProvider是在组件的return语句中才渲染的。React Context的作用范围仅限于Provider包裹的子组件,在Provider外层调用useContext会拿到Context的默认值(这里你设置的是null),所以会出现dispatch is not a function的错误,同时counter也无法获取到初始值0。
修复方案
将需要使用counter和dispatch的UI逻辑抽离成独立的子组件,让这个子组件被CounterProvider包裹,确保Hook在Context的作用域内调用。
修改后的代码
App.js
import { useEffect } from "react"; import { CounterProvider } from "./CounterContext"; import { useCounter, useCounterDispatch } from "./CounterContext"; // 抽离子组件,放在CounterProvider内部使用 function CounterControls() { const counter = useCounter(); const dispatch = useCounterDispatch(); useEffect(() => { console.log("counter: ", counter); console.log("dispatch: ", dispatch); }, [counter, dispatch]); // 添加依赖项,避免无限渲染 return ( <div> <button onClick={() => dispatch({ type: "increment" })}> increment </button> <button onClick={() => dispatch({ type: "decrement" })}> decrement </button> <button onClick={() => dispatch({ type: "reset" })}> reset </button> <h3>{counter}</h3> </div> ); } export default function CounterApp() { return ( <CounterProvider> <h2>Counter App</h2> <CounterControls /> </CounterProvider> ); }
CounterContext.js(可选优化)
import { createContext, useContext, useReducer } from "react"; const CounterContext = createContext(null); const CounterDispatch = createContext(null); export function CounterProvider({ children }) { const [counter, dispatch] = useReducer(counterReducer, initialCounter); return ( <CounterContext.Provider value={counter}> <CounterDispatch.Provider value={dispatch}> {children} </CounterDispatch.Provider> </CounterContext.Provider> ); } export function useCounter() { return useContext(CounterContext); } export function useCounterDispatch() { return useContext(CounterDispatch); } // 优化reducer,去掉不必要的赋值操作 function counterReducer(counter, action) { switch (action.type) { case "increment": return counter + 1; case "decrement": return counter - 1; case "reset": return 0; default: throw Error("Unknown action: " + action.type); } } const initialCounter = 0;
额外说明
- 抽离子组件后,
CounterControls处于CounterProvider的作用域内,调用useCounter和useCounterDispatch时能正确获取到上下文提供的状态和dispatch函数。 - 给
useEffect添加依赖项是符合React Hook规则的做法,避免组件每次渲染都触发不必要的日志输出。 - reducer中直接返回新状态值,比使用赋值语句更简洁规范,符合useReducer的使用习惯。
内容的提问来源于stack exchange,提问作者Nasem
相关产品推荐
相关产品推荐

