【发布时间】:2019-11-04 12:32:51
【问题描述】:
我有一个严重依赖 redux、react-redux 和 redux-saga 的 React 应用程序。我开始尝试使用 React Hooks,以及 react-redux 中的 useSelector 和 useDispatch 钩子,我遇到了一个问题,即如何编写一个工厂函数来为我的每个 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 },
];
这描述了三个动作,get、post和clear,它们应该在连接器中公开。我有一组常见的减速器(例如,大多数 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