React Hooks 是不能替代 Redux 的

阅读数:4396 2019 年 9 月 3 日 08:30

React Hooks是不能替代Redux的

我的许多同事最近通过各种方式问同一类问题:
“如果我们开始用 hook 后,那还有必要用 Redux 吗?”

“React hook 不是让 Redux 过时了吗?那只用 Hooks 就可以做 Redux 所有能做的事了吧?”

随便搜一下 Google,你就会发现人们也在互联网上问同样的问题。

简单来说,“React Hooks 是否替代了 Redux”这个问题的答案是:“不”。
更细致不过礼貌的答案是:“嗯,这个取决于你实际项目的类型“。
最终,我倾向于回答人们“我不确定你是否明白自己在说什么”。

“React Hooks 取代了 Redux”这个论点有着根本的缺陷,其原因有好几个。首先:

Redux 从始至终都是可选方案

Redux 的作者之一 Dan Abramov 曾经表达过你可能不需要 Redux 的建议。如果你本来就不需要某样东西,那你也就不用替换它了。

如果你正在用 React,为了用起来 Redux,你还得把 Redux-Redux 装到你的应用里。在项目中使用多个库显然会增加最终应用打包的大小,也就会增加加载应用所需的时间。因此,除非有真正的理由,你不应该使用任何库,如 jQuery,Redux,MobX,甚至是 React。

当人们问起来 hook 是否能替代 Redux 时,他们似乎认为得在这两者之间做出选择,但事实并非如此。如果你正开发的应用不需要存储大量状态,或者组件层次足够简单不需要深层次的 prop 传递,那么用整个状态管理库方案是没有意义的。无论是否使用 hook,应用的状态都足够由 React 提供的功能进行管理了。

即便你的应用需要管理一个庞大的状态,或者应用的层次结构如古树的根一样错综复杂,你也不一定非得使用状态管理库。深层次传递 prop 可能很麻烦,但原生的 React 已经为你提供了包含在 hook 在内的多种状态管理的选择,他们都能帮你把状态管理的井井有条。Redux 是一个轻量级的库,不过配置相对复杂,增加最终打包的体积,还需要做出各种权衡取舍。不在项目中使用 Redux 有许多合理的理由,所以你并不总是需要用它。

虽然如此,我们还是有许多理由使用 Redux。如果你的项目本来就在用 Redux,那最开始决定使用 Redux 应该是有一些好的理由的,比如项目的组织架构需要有一个可预测的,单一事实来源的程序状态;中间件功能;或是强大的开发调试工具。如果你曾有理由使用 Redux,那 hook 并不会让你的这些理由失效。如果你曾需要用 Redux,那你也许还得继续用它。

React hook 尝试解决问题与 Redux 不同

Redux 是一个状态管理库。Hooks 是最近才加入 React 的新功能,它可以让函数组件支持曾经只在 class 组件中支持的特性。

那么,用函数组件替换 class 组件实现 React 应用会让状态管理库过时吗?答案是否定的。

关于为何开发 React hook,官方文档给出了三个主要原因:

  • class 组件的逻辑复用很困难。

  • 生命周期方法内常包含让人困惑的无关逻辑。

  • class 对于机器和人类来说都难以理解。

你可以注意到这些动机都跟状态管理无关。

话虽如此,React hook 确实为你提供了状态管理的新选择。值得注意的是,useState,useReducer 和 useContext 这些管理状态的新方法,一定程度上比之前 React 原生提供的方案更好、更有条理。但 React hook 没有提供超出之前 React 版本的新能力,也不会让状态管理库过时。

React hook 不会使 React 应用做任何它以前做不到的事

没错,函数组件可以做到以前只有在 class 组件才能实现的功能,同时函数组件有更好的代码组织结构和重用能力,但函数组件做不到 class 组件也做不到的事。React hook 的目的不是为了让应用更好,而是为了让开发体验更好。

useState 和 useReducer 只是管理组件状态的方法,其工作方式与 class 组件中的 this.state 和 this.setState 工作方式的大致相同。对于深层传递 prop 的问题,hook 也是无能为力。

人们似乎认为 useContext 可以把 Redux 打入冷宫,因为你可以使用它解决状态深层次传递的问题,但它确实不是什么新功能。 context API 已经存在于 React 一段时间了。useContext 只是让你可以不通过包装组件就能使用 context。虽然有些开发人员选择使用 context 管理整个应用的状态,但 context 的设计初衷不是如此。官方文档提到:

Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。

换句话说,放在 context 中的数据不应该经常更新。

文档还建议要谨慎使用 context,因为“它会让代码复用变得更困难“。他们还警告开发人员,如果使用不当,context 还会导致不必要的重新渲染。

我见到过一些项目成功使用 React context 管理整个应用的状态。理论上这确实是一种选择。但 context 的设计初衷并不包含状态管理,而这是 Redux 或是其他状态管理库的设计目的。
此外,React hook 绝对不意味着 Redux 的灭亡,如果你瞅一眼 React-Redux 最近更新的文档,你会看到:

React-Redux 也拥有自己的 hook 了

没错,React hook 有助于重振 React-Redux 库,并移除了一些它的痛点。这与“hook 会替代 Redux”这个观点相去甚远。

我在另一篇文章深入介绍了 React-Redux 中的 hook 。在引入 hook 之前,你必须定义 mapStateToProps 与 mapDispatchToProps 函数、并用 connect 函数包装你的组件来创建出一个高阶组件。该高阶组件会把 dispatch 函数与 Redux store 的一部分状态通过你定义的映射函数作为 props,传到被 conntext 包装的组件中。

我们来看个非常简单的计数器应用示例(实际上因为过于太简单不必使用 Redux,但这个例子是为了表达一下信息的概述)。假设我们已经定义了 Redux store,并在其他地方定义了增加和减少的动作创建方法。

真是繁琐。如果我们可以不用把组件包装在高阶组件内就能让组件访问到 Redux store,不更好吗?没错,这就是 hook 可以发挥的地方了。hook 其主要功能就是代码复用,同时消除高阶组件导致的“包装地狱”问题。

简而言之,useSelector 允许你将 Redux store 的各个状态切片保存为组件的变量。useDispatch 非常简单,你可以通过它发出动作更新 Redux store。最重要的是,你不再需要实现丑陋的映射函数,并把组件包装在 connect 函数中了。现在,一切都很好的包含在你的组件里。这样的实现更简短,也就更具有可读性、更有条理。

React hook 和 Redux 不是互为竞争关系的技术

显然,这两种技术可以相互补充。React hook 不会“替换 Redux”,它只是为你提供了更新、也许更好的方式组织你的 React 应用;如果你最终决定使用 Redux 管理应用状态,也许能写出更高内聚的组件。

所以,请不要再问 "React hook 是否取代了 Redux?" 的问题了。

相反,请问一下自己:“我在开发怎样的应用?我需要怎么样的状态管理需求?Redux 是否对我来说是合理的方案,或者它对于我的需求过度复杂了?如果我决定使用 Redux 和 React hook(或是 MobX+Hook, Redux+jQuery 等技术组合)我怎样才能让这些技术相互补充,和谐工作呢?”

英文原文: https://medium.com/swlh/stop-asking-if-react-hooks-replace-redux-448c54d79551

评论

发布