【问题标题】:Is it possible to create a React Hook factory function?是否可以创建 React Hook 工厂函数?
【发布时间】:2019-11-04 12:32:51
【问题描述】:

我有一个严重依赖 reduxreact-reduxredux-saga 的 React 应用程序。我开始尝试使用 React Hooks,以及 react-redux 中的 useSelectoruseDispatch 钩子,我遇到了一个问题,即如何编写一个工厂函数来为我的每个 redux 节点生成钩子.

一般来说,我的应用程序使用的每个 API 端点都有一个 redux 节点。这个应用程序中有大约 100 个唯一端点,因此大约有 100 个 redux 节点。然后每个节点对应一个state-[node].js 文件,例如state-users.js 等。这些状态文件每个都封装了它们应该调用的端点,触发sagas 来处理HTTP 生命周期(启动、失败、成功等),等等开。

随着时间的推移,我编写的代码将大部分样板代码抽象为实用函数,包括自动生成动作创建器、选择器、缩减器的函数和connect 函数。这是一堆代码,有些令人费解,但要点看起来像这样。首先,我设置了一个对象数组,描述了这个 redux 节点可能执行的操作。简化版如下所示:

const messages = [
  { action: 'GET', creator: 'get', connect: true },
  { action: 'POST', creator: 'post', connect: true },
  { action: 'CLEAR', creator: 'clear', connect: true },
];

这描述了三个动作,getpostclear,它们应该在连接器中公开。我有一组常见的减速器(例如,大多数 get 减速器在节点之间是相同的),所以这里是根据名称假设的。

然后我设置了一个选择器列表,如下所示: const selectorKeys = ['data','pending','errors'];

...然后我有一个工厂函数,我将这些数组输入其中,它看起来像这样:

const { connector } = stateGenerators({
   keyword: 'users', //redux node keyword
   messages: messages,
   selectorKeys: selectorKeys

}) 

这是一切如何真正运作的简化版本,但它是它的症结所在。同样,上述所有代码都被抽象为一个状态文件,例如state-users.js

然后,在我的类组件中,我只需要从state-users.js 导入connector,如下所示:

import { connector } from 'state-users';

class Users extends Component {
    componentDidMount() {
       this.props.get();
    }
    componentWillUnmount() {
      this.props.clear();
    }
    render () {
      const { data } = this.props;
      return (
        <div>
        {data.map()}
        </div>

      )
    }
}

export connector()(Users)

这个模型有时确实很笨拙,但好的部分是几乎所有的 redux 样板都被抽象成通用文件,所以我的个人状态文件在大多数情况下非常简单。

现在,问题是:是否可以使用 Hooks 执行类似“工厂函数”的方法?到目前为止,在我的实验中,我还没有弄清楚如何做到这一点。有两个基本问题:

  • 首先,你不能把钩子放在循环中,所以我不能这样做:
const selectors = {}
const reduxNodeKeyword = 'users';
['data','pending','errors'].map((selectorKey) => {
   selectors[selectorKey] = useSelector((state) => state[keyword].selectorKey);
})

该代码导致此错误: React hook "useSelector" cannot be called inside of a callback.

实际上,这意味着我不能只传入我想要的选择器键数组,然后让它吐回我的选择器。

  • 其次,您不能将钩子放在条件句中。所以由于第一个想法失败了,我在我的工厂函数中尝试了一种不同的方法,它看起来像这样:
 if (_.includes(stateSelectors, 'data')) {
    result['data'] = useSelector((state) => state[keyword].data);
 }

这会导致此错误: React hook "useSelector" is called conditionally. React Hooks must be called in the exact same order in every component render

所以这也是一个无赖。但我认为我剩下的就是对于我的 100 个 Redux 节点中的每一个,我都必须编写一个自定义的、冗长的钩子来或多或少地复制 connect

我知道,我知道,钩子应该鼓励我以不同的方式思考,但我仍然需要从服务器获取数据并将其提供给组件,并且我需要遵循相同的基本模式 100 次。即使 hooks 让我的组件更优雅(正如我所期望的那样),手动编写 100 个左右的 hooks 的想法,每个都有相当数量的重复数据,而不是使用某种工厂方法以某种方式自动创建它们,让我荨麻疹。

帮助?

谢谢!

【问题讨论】:

  • 你能在connector()(Users)之后显示你想要的行为吗,你不是在描述connect函数吗?
  • 你不能在功能组件中使用连接的东西。我希望能够通过功能组件中的钩子连接到 redux。
  • ...是的,我想我正在描述连接的钩子版本。但更具体地说,我尽量避免使用钩子样板。我想我了解如何编写连接的钩子版本,但我不想最终重复相同的冗长钩子代码 100 倍以上,因此希望有某种工厂解决方案。
  • 在搜索“react hook factory”时发现了这个问题,很惊讶我们正在做同样的事情:)
  • @AndrewCraswell FWIW,我现在有点讨厌钩子工厂模式,至少对我自己来说是这样,因为我发现它太抽象了,我总是不得不研究挂钩工厂函数以查看该死的东西做了什么。它为我节省了几行代码,但增加了我的整体困惑。一如既往,YMMV。 (-;

标签: reactjs react-redux react-hooks


【解决方案1】:

不确定这对其他人是否有用,但我找到了一种适合我的方法。我不能把钩子放在迭代器中,也不能放在if 语句中,但我确实在所有地方都有共同的模式。所以答案是将那些常见的模式抽象成通用的钩子。例如,我有一个名为useCommonReduxListSelectors 的钩子,它看起来像这样:

export const useReduxListCommonSelectors = (keyword: string) => {
  return {
    data: useSelector((state: any) => state[keyword].data),
    errorMessage: useSelector((state: any) => state[keyword].errorMessage),
    filters: useSelector((state: any) => state[keyword].filters),
    pending: useSelector((state: any) => state[keyword].pending),
    totalCount: useSelector((state: any) => state[keyword].totalCount)
  };
};

我有很多 Redux 状态节点,它们负责处理从 API 返回的列表,并且大多数列表端点的数据形状就是您在上面看到的。那么组件将调用的钩子使用useReduxListCommonSelectors,如下所示:

export const useReduxState = ({ id, defaultValues }) => {
  const selectors = useReduxListCommonSelectors({
    keyword:'users'
  });

  return {
    ...selectors,

  };
};

然后显然useReduxState 可以在其中包含该节点所需的任何其他数据(actionCreators、自定义选择器等)。因此它允许我将最常见的模式抽象为可重用的钩子,并且还可以根据需要灵活地将自定义代码添加到每个 useReduxState 实例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-25
    相关资源
    最近更新 更多