Skip to the content.

Blogged Answers: 对 React Hooks, Redux 和注意点分离的思考

发布于 2019 年 7 月 10 日

这篇文章属于 Blogged Answers 系列

关于使用 React Hooks 所涉及的“权衡利弊”的一些看法

介绍

最近我看到很多关于 React Hooks 使用的问题,特别是 React-Redux hooks。最常见的问题是这样的:

实际上,我对 Hooks 带来的”权衡利弊”有很多想法。今天早些时候,我在 Twitter 上写了一个很长的 thread,其中有一些想法,我想在这里扩展一下这个 thread。

在我进一步阐述之前,有一些注意事项。我已经写了一些使用 Hooks 的小例子,但我还没有在真实世界的应用中认真使用它们。特别是我们新的 React-Redux Hooks API 也是如此–我推动了设计和发布这些 API 的工作,但我自己只简单地在实际中使用过它们。我也不是测试方面的专家。我知道很多理论,只是我自己没有实际写过那么多测试(单元或集成)。所以,我想说的是,这些只是一些观点和观察,我很可能是错的,你应该对事情做出自己的判断。

Hooks、HOC(High Order Components) 和权衡利弊

自从 Hooks 被宣布后,React 社区就为其疯狂了。虽然有很多合理的怀疑,但社区中的大部分人都对 Hooks 跃跃欲试,把它们吹捧为未来编写 React 代码的 “理所应当的方式”。通常来说,应该用一些更现实的期望来缓和这种突变和激动。

从某种意义上说,Hooks 并没有给你带来什么新东西。class 组件一直都有状态和副作用。Hooks 只是让你在函数组件中做同样的事情。从这个意义上说,什么都没有改变。

同时,Hooks 又极大地改变了一些事情。原本分割在多个生命周期中的逻辑现在被集中在一起。Hooks 需要使用和了解闭包,而不是this。同样的结果,但写法完全不同。特别是,使用闭包的结果是组件要大得多,因为你必须写内联函数来捕获作用域范围内的变量。

与任何技术一样,这也是一种权衡利弊。集中在一起通常是好的,但复杂的组件可能会非常长。有些可以通过提取自定义 Hooks 来缓解。(我想你可以用内联定义函数来做类似的事情–有一些像 makeClickHandler(a,b,c)这样的工厂函数来把实际的代码移到组件之外,但这开始变得有点愚蠢和过度抽象了。)

正因为如此,Hooks 在 “关注点分离 “和 “简单性 “方面有意做出了与 HOC 不同的权衡利弊。

在这个意义上,Hooks 故意远离了 “关注点分离”。一个组件现在明确地期望从某个地方读取它自己的数据并渲染它。你可以为获取数据和渲染编写单独的组件,但是也意味着你已经重新发明了 HOC。

React-Redux Hooks 的使用模式

Redux 团队一直提倡 “保持组件对 Redux 无意识”。

它们应该像其他 React 组件一样,简单地接收数据和函数作为 props。这最终使得测试和重用你自己的组件更加容易。

现在,我们的新 Hooks API 并没有彻底改变什么。我们一直鼓励使用选择器函数(selector 函数)从存储状态中提取值–useSelector()只是把它正式化了(你甚至可以用useSelector()重用你现有的mapState函数,前提是你要么把结果暂存(memoize)下来,要么用shallowEqual作为比较函数)。

同时,你一直可以只使用 connect()(MyComponent),在组件中做一些异步逻辑,然后显式使用 this.props.dispatch(someAction)。然而,我们不鼓励这样做,理由是它使异步代码的可重用性降低

一般来说,Redux 建议带有副作用的代码应该是 action 创建过程的一部分。虽然该逻辑可以在 UI 组件内执行,但一般来说,将该逻辑提取到一个可重用的函数中是有意义的,这样可以从多个地方调用相同的逻辑(同一个 action creator function)。

这也是action creators、thunks 和 mapDispatch 存在的部分原因:在调用现场没有明确提到 dispatch 的情况下实现 dispatch,并实现通用的展示型组件。

不过,在我们的 React-Redux Hooks 中,你只有 useDispatch()。所以,你总是有直接可用的东西。人们可能会放弃 thunks(redux-thunk),在 useEffect()中编写异步逻辑,并直接在组件中进行dispatch。通过提取Custom hooks,它还可以使 React 和 Redux 以一些新的方式混合在一起,可以参考这个从 connect()转换到 hooks 的例子

(题外话: “容器型/展示型 “一直被社区过度解读。Dan(译者注:原 Redux 开发者,现 React 核心团队开发人员)后来很大程度上否认了他的原帖,当我们修改 Redux 文档时,我们会重写 “React 使用 “教程页面,不再强调这个概念。)

所以是的,我们的 React-Redux Hooks 肯定会将组件与 Redux 更紧密地结合在一起,因为对于任何使用 context 而不是 props 的组件来说都是这样的。组件会对上下文实例和所提供的数据有一种隐含的依赖性。

结论

就像大多数编程一样:不是说这些方法中的任何一个是对的或错的。而是这些方法有不同的权衡,每个团队都必须做出自己的决定,哪些权衡对他们更重要。

你想在多大程度上分离关注点?如果你喜欢”shallow”测试组件(译者注:比如 Enzyme 的 shallow),Hooks 可能不适合你,因为它们真的需要“集成”类型的测试。你是喜欢更干净的组件树、集中和更容易的静态类型,还是更多的关注点分离和间接性?

从长远来看,看到这个问题在生态系统中的影响将是非常有趣的。Hooks 只出现了几个月,所以社区仍在努力解决如何最好地利用它们的问题。我们花了很多年才从 “mixins “到 “HOCs”,然后发现”render props”是什么东西。我们将在很长一段时间内探索 Hooks 的使用模式。

另外一个想法:我经常观察到,React 和 Redux 的用户可以大致分为两种观点。”以应用为中心 “与 “以组件为中心 “的设计。我认为围绕 Hooks 的权衡问题与这些思维方式的一些方面有关。React 是你真正的”应用”吗?或者它只是 UI 层,而真正的应用是保存在组件树之外的逻辑和数据?这两种观点都是很有道理的,同样有不同的权衡。

更多

也请看我在 ReactBoston 2019 的演讲 “Hooks, HOCs, and Tradeoffs”,其中进一步讨论了这个话题。