我可以从外部组件使用useReducer吗

pun*_*pun 5 javascript ecmascript-6 reactjs react-hooks

现在,我试图使用useReducer创建一种管理状态和功能的新方法,但是现在发现问题是“只能在函数组件的主体内部调用挂钩”,有什么办法可以解决此问题?

// App Component
import React from "react";

import { product, productDis } from "./ProductReducer";
//{product} is state,  {productDis} is dispatch

import { total } from "./TotalReducer";
//{total} is state and i dont need {totalDis}


const App = () => {
  return (
    <div>
      <button onClick={()=>productDis({type:'add',payload:'pen'})}>add</button>
      {product} {total}
    </div>
  );
};
export default App;
Run Code Online (Sandbox Code Playgroud)
// ProductReducer Component
import React, { useReducer } from 'react';
import {totalDis} from './TotalReducer'
//{totalDis} is dispatch and i dont need {total}


export const [product, productDis] = useReducer((state, action) => {
    switch (action.type) {
        case "add": {
            const product_0 = 'pencil'
            const product_1 = `${action.payload} and ${product_0}`
            totalDis({
                type:'total_add',
                payload:'250'
            })
            return product_1;
        }
        default:
            return state;
    }
}, []);
Run Code Online (Sandbox Code Playgroud)
// TotalReducer Component
import React, { useReducer } from 'react';

export const [total, totalDis] = useReducer((total, action) => {
    switch (action.type) {
        case "total_add": {
            const vat = action.payload*1.15
            return vat;
        }
        default:
            return total;
    }
}, 0)
Run Code Online (Sandbox Code Playgroud)

当我单击显示屏上的按钮时,应该显示...“钢笔和铅笔287.5”

但显示“只能在函数组件的主体内部调用挂钩”

有什么办法解决这个问题?还是我应该回归自然?

Est*_*ask 5

React挂钩只能在功能组件内部调用。挂钩状态是每个组件实例维护的。如果必须重用钩子,则可以将它们提取到自定义钩子中,这些钩子是调用内置钩子的函数,应该在功能组件内部调用:

export const useTotal = () => {
  const [total, totalDis] = useReducer((total, action) => {...}, 0);
  ...
  return [total, totalDis];
};
Run Code Online (Sandbox Code Playgroud)

如果需要维护多个组件的公共状态,则应将其维护在公共父级中,并通过道具提供给孩子:

const Root = () => (
  const [total, totalDispatcher] = useTotal();

  return <App {...{total, totalDispatcher}}/>
);

const App = props => {
  return (
    <div>{props.total}</div>
  );
};
Run Code Online (Sandbox Code Playgroud)

或上下文API:

const TotalContext = createContext();

const Root = () => (
  <TotalContext.Provider value={useTotal()}>
    <App/>
  </TotalContext.Provider>
);

const App = () => {
  const [total] = useContext(TotalContext);
  return (
    <div>{total}</div>
  );
};
Run Code Online (Sandbox Code Playgroud)

  • 这不是问题的答案。 (2认同)

Bho*_*yar 0

文档中,

\n\n

您可能会看到它的三个常见原因:

\n\n
    \n
  • 您的 React 和 React DOM 版本可能不匹配。
  • \n
  • 您可能违反了 Hooks 规则。
  • \n
  • 您可能在同一个应用程序中拥有多个 React 副本。
  • \n
\n\n

深入了解文档。我希望您能够解决这个问题。特别参见:

\n\n

打破 Hook 规则:

\n\n
function Counter() {\n  // \xe2\x9c\x85 Good: top-level in a function component\n  const [count, setCount] = useState(0);\n  // ...\n}\n\nfunction useWindowWidth() {\n  // \xe2\x9c\x85 Good: top-level in a custom Hook\n  const [width, setWidth] = useState(window.innerWidth);\n  // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您违反这些规则,您可能会看到此错误。

\n\n
function Bad1() {\n  function handleClick() {\n    //  Bad: inside an event handler (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n  }\n  // ...\n}\n\nfunction Bad2() {\n  const style = useMemo(() => {\n    //  Bad: inside useMemo (to fix, move it outside!)\n    const theme = useContext(ThemeContext);\n    return createStyle(theme);\n  });\n  // ...\n}\n\nclass Bad3 extends React.Component {\n  render() {\n    //  Bad: inside a class component\n    useEffect(() => {})\n    // ...\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

总而言之,您的错误似乎看起来就像您在点击处理程序中使用减速器一样。检查示例Bad1来解决您的问题。我的意思是你不应该这样做:

\n\n
onClick={()=>productDis({type:\'add\',payload:\'pen\'})}\n
Run Code Online (Sandbox Code Playgroud)\n\n

在 onClick 处理程序中,分派操作并在方法内部使用该化简器。

\n