dispatch is not a function in react for a simple counter app

huangapple go评论148阅读模式
英文:

dispatch is not a function in react for a simple counter app

问题

我正在尝试理解React的上下文(context)和Reducer钩子(hooks),并编写一个简单的计数器应用程序。在这个应用程序中,我需要三种功能:增加、减少和重置。

但是,当我点击这些功能的按钮时,控制台显示错误消息'dispatch is not a function'。此外,来自上下文的计数器变量在浏览器中不显示任何值,尽管它应该显示初始值为0,因为我设置了它的值。

以下是应用程序的代码:

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 + 1;
    }

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

    case "reset": {
      return 0;
    }

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

const initialCounter = 0;

注意:在counterReducer函数中,不需要将counter重新赋值,只需返回新的计数器值。

英文:

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 &quot;react&quot;;
import { CounterProvider } from &quot;./CounterContext&quot;;
import { useCounter, useCounterDispatch } from &quot;./CounterContext&quot;;

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

  useEffect(() =&gt; {
    console.log(&quot;counter: &quot;, counter);
    console.log(&quot;dispatch: &quot;, dispatch);
  });

  return (
    &lt;CounterProvider&gt;
      &lt;h2&gt;Counter App&lt;/h2&gt;
      &lt;div&gt;
        &lt;button
          onClick={() =&gt; {
            dispatch({ type: &quot;increment&quot; });
          }}
        &gt;
          increment
        &lt;/button&gt;
        &lt;button
          onClick={() =&gt; {
            dispatch({ type: &quot;decrement&quot; });
          }}
        &gt;
          decrement
        &lt;/button&gt;
        &lt;button
          onClick={() =&gt; {
            dispatch({ type: &quot;reset&quot; });
          }}
        &gt;
          reset
        &lt;/button&gt;
        &lt;h3&gt;{counter}&lt;/h3&gt;
      &lt;/div&gt;
    &lt;/CounterProvider&gt;
  );
}

CounterContext.js:

import { createContext, useContext, useReducer } from &quot;react&quot;;

const CounterContext = createContext(null);
const CounterDispatch = createContext(null);

export function CounterProvider({ children }) {
  const [counter, dispatch] = useReducer(counterReducer, initialCounter);

  return (
    &lt;CounterContext.Provider value={counter}&gt;
      &lt;CounterDispatch.Provider value={dispatch}&gt;
        {children}
      &lt;/CounterDispatch.Provider&gt;
    &lt;/CounterContext.Provider&gt;
  );
}

export function useCounter() {
  return useContext(CounterContext);
}

export function useCounterDispatch() {
  return useContext(CounterDispatch);
}

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

    case &quot;decrement&quot;: {
      return counter = counter - 1;
    }

    case &quot;reset&quot;: {
      return 0;
    }

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

const initialCounter = 0;

答案1

得分: 2

你不能在CounterApp组件内部使用Counter和Dispatch上下文,因为它没有包裹在提供者内部。你的CounterApp组件渲染了提供者,所以你需要再创建一个组件,它将消费这个上下文并将其作为CounterProvider的子组件传递,例如:

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>
    </>
  );
}
英文:

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 &quot;react&quot;;
import { CounterProvider } from &quot;./CounterContext&quot;;
import { useCounter, useCounterDispatch } from &quot;./CounterContext&quot;;

export default function CounterApp() {
  return (
    &lt;CounterProvider&gt;
      &lt;CounterAppInner /&gt;
    &lt;/CounterProvider&gt;
  );
}

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

  useEffect(() =&gt; {
    console.log(&quot;counter: &quot;, counter);
    console.log(&quot;dispatch: &quot;, dispatch);
  });

  return (
    &lt;&gt;
      &lt;h2&gt;Counter App&lt;/h2&gt;
      &lt;div&gt;
        &lt;button
          onClick={() =&gt; {
            dispatch({ type: &quot;increment&quot; });
          }}
        &gt;
          increment
        &lt;/button&gt;
        &lt;button
          onClick={() =&gt; {
            dispatch({ type: &quot;decrement&quot; });
          }}
        &gt;
          decrement
        &lt;/button&gt;
        &lt;button
          onClick={() =&gt; {
            dispatch({ type: &quot;reset&quot; });
          }}
        &gt;
          reset
        &lt;/button&gt;
        &lt;h3&gt;{counter}&lt;/h3&gt;
      &lt;/div&gt;
    &lt;/&gt;
  )
}

huangapple
  • 本文由 发表于 2023年2月19日 01:18:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/75495040.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定