【问题标题】:Can I use useReducer from outside component我可以从外部组件使用 useReducer
【发布时间】:2019-08-14 18:46:43
【问题描述】:

现在我正在尝试使用 useReducer 创建管理状态和函数的新方法,但现在发现问题是“只能在函数组件的主体内部调用 Hooks” 有什么办法可以解决这个问题吗?

// 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;
// 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;
    }
}, []);
// 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)

当我点击显示屏上的按钮时,它应该会显示..." pen and pencil 287.5"

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

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

【问题讨论】:

标签: javascript reactjs ecmascript-6 react-hooks


【解决方案1】:

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

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

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

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

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

const App = props => {
  return (
    <div>{props.total}</div>
  );
};

或上下文 API:

const TotalContext = createContext();

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

const App = () => {
  const [total] = useContext(TotalContext);
  return (
    <div>{total}</div>
  );
};

【讨论】:

  • 这不是问题的答案。
  • @BryanGrace 不能说任何问题,但它肯定回答了上面的问题 - 是否可以在组件内部使用 useReducer 以及解决方法是什么。如果您有不明白的地方,您可以要求澄清。
【解决方案2】:

来自docs

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

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

深入了解文档。我希望,您将能够解决问题。尤其看:

打破挂钩规则:

function Counter() {
  // ✅ Good: top-level in a function component
  const [count, setCount] = useState(0);
  // ...
}

function useWindowWidth() {
  // ✅ Good: top-level in a custom Hook
  const [width, setWidth] = useState(window.innerWidth);
  // ...
}

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

function Bad1() {
  function handleClick() {
    // ? Bad: inside an event handler (to fix, move it outside!)
    const theme = useContext(ThemeContext);
  }
  // ...
}

function Bad2() {
  const style = useMemo(() => {
    // ? Bad: inside useMemo (to fix, move it outside!)
    const theme = useContext(ThemeContext);
    return createStyle(theme);
  });
  // ...
}

class Bad3 extends React.Component {
  render() {
    // ? Bad: inside a class component
    useEffect(() => {})
    // ...
  }
}

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

onClick={()=>productDis({type:'add',payload:'pen'})}

在 onClick 处理程序中,调度操作并在方法内部使用该 reducer。

【讨论】:

  • 我喜欢 React 开发人员决定你不应该这样做而应该那样做的方式。为什么这两种方法都不起作用是没有意义的。
【解决方案3】:

使用useEnhancedReducer 钩子引入here,它返回getState 函数。

你会有类似的东西。

const [state, dispatch, getState] = useEnahancedReducer(reducer, initState)

因为dispatchgetState 永远不会改变,它们可以在不出现在依赖列表中的情况下用于某些钩子中,它们可以存储在其他地方(react 之外)以便随时随地调用.

还有useEnhancedReducer的版本支持添加中间件,在同一篇文章中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-26
    • 2021-03-11
    • 1970-01-01
    • 2017-03-15
    • 1970-01-01
    • 1970-01-01
    • 2015-11-30
    相关资源
    最近更新 更多