1

I am trying to understand the react context and reducer hooks and coding a simple counter app. In this app, I need three functionality: increment, decrement, and reset.

But when I click the buttons for those functionalities, the console shows the error 'dispatch is not a function'. Also, the counter variable from the context doesn't show any value in the browser where it should show the initial value of 0 because that's what I set it to be.

Here's the code for the app:

App.js

import { useEffect } from "react";
import { CounterProvider } from "./CounterContext";
import { useCounter, useCounterDispatch } from "./CounterContext";

export default function CounterApp() {
  const counter = useCounter();
  const dispatch = useCounterDispatch();

  useEffect(() => {
    console.log("counter: ", counter);
    console.log("dispatch: ", dispatch);
  });

  return (
    <CounterProvider>
      <h2>Counter App</h2>
      <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>
    </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);
}

function counterReducer(counter, action) {
  switch (action.type) {
    case "increment": {
      return counter = counter + 1;
    }

    case "decrement": {
      return counter = counter - 1;
    }

    case "reset": {
      return 0;
    }

    default: {
      throw Error("Unknown action: " + action.type);
    }
  }
}

const initialCounter = 0;
Nasem
  • 417
  • 2
  • 7
  • 15
  • Does this answer your question? [useDispatch() Error: Could not find react-redux context value; please ensure the component is wrapped in a ](https://stackoverflow.com/questions/60329421/usedispatch-error-could-not-find-react-redux-context-value-please-ensure-the) – Brian Thompson Feb 18 '23 at 18:06

1 Answers1

2

You can't use the Counter and Dispatch Contexts inside the CounterApp component, because it's not wrapped inside the provider. Your CounterApp component renders the provider, so you'll need to create one more component that will consume this context and pass it as a child of CounterProvider, for example:

import { useEffect } from "react";
import { CounterProvider } from "./CounterContext";
import { useCounter, useCounterDispatch } from "./CounterContext";

export default function CounterApp() {
  return (
    <CounterProvider>
      <CounterAppInner />
    </CounterProvider>
  );
}

function CounterAppInner() {
  const counter = useCounter();
  const dispatch = useCounterDispatch();

  useEffect(() => {
    console.log("counter: ", counter);
    console.log("dispatch: ", dispatch);
  });

  return (
    <>
      <h2>Counter App</h2>
      <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>
    </>
  )
}
thelukaszns
  • 398
  • 4
  • 11