React Hooks 设计思想
作者:繁星https://zhuanlan.zhihu.com/p/103692400
聊聊 React 的 class 组件
组件是 React 应用的构建块,自上而下的数据流结合组件可以将 UI 解构为独立且可复用的单元。组件主要做的事情主要有以下三点:
将传入的 props 和 内部 state 渲染到页面上;
管理内部 state,并根据 state 变化渲染出最新的结果;
处理与组件外部的交互;
假如现在有一个新闻列表页面,列表的每一项都包含有标题、概要、详情和缩略图,如图所示:
只是渲染内容。如果不考虑查看详情这个交互,新闻列表的每一项是很纯的,也就是 props 传入什么数据,就能渲染出一一对应的结果:
要考虑查看详情这个交互,就必须在 NewsItem 里加入一个 isDetailShow 的 state 来表示新闻摘要与详情的互斥显示。到目前为止,NewsItem 还是很纯的,并没有和外部有交互。
综上,我们的组件需要 state 来存储一定的逻辑状态,并且需要可以访问并更改 state 的方法函数。
这样通过 class 组件,ReactDOM 就能做到渲染数据,绑定事件,并在不同的生命周期调用开发者所编写的代码,按需求将数据渲染成 HTML DOM,然后被浏览器渲染展示出来。
将组件渲染粗暴地分为若干个阶段,通过生命周期函数处理副作用会带来一些问题:
同一职责代码有可能需要被强行分拆到不同的生命周期,例如同一个事件的订阅与取消订阅;
一部分代码被分割到不同生命周期中,会导致组件没有优雅的复用 state 逻辑代码的能力,高阶组件或 render props 等模式引入了嵌套,复杂且不灵活;
越来越多逻辑被放入不同生命周期函数中,这种组织方式导致代码越来越复杂难懂;
除了这些,class 组件中的 this 也常被人们拿出来吐槽。那么,是否有更优雅的设计呢?
闭包为什么在某种程度上能取代 class?
为了实现复用,我们将具有特定单一功能的逻辑放在函数里,这样既可以消灭掉重复代码,又可以让我们在思考问题时能够进行合理的分解,降低代码复杂度。
但是只有函数是不够的,函数是一个标准的输入-加工-输出模型,输入和输出的都是变量里所存储的数据,当一个系统的复杂度高到一定程度的时候,将函数与其所操作的数据(环境)关联起来就很有必要了。
看,我们使用闭包将变量 privateCounter 与几个函数关联了起来,从这点来讲能力与面向对象编程相同。
组件的 API 设计
API 的核心在于表达能力,对于 React 组件来说,就是如何让开发者将需求良好地表达出来,然后被 ReactDOM 识别并渲染。
React Hooks 原理
正是因为 hooks 是这样实现的,我们在调用 hooks 的时候必须要严格保证每一次 render 都能获得一致的执行顺序,所以必须要做到:
完整简化代码地址:https://stackblitz.com/edit/behind-react-hook
useEffect 提供了一个函数(上面代码中的 cb)运行的容器,这个容器有以下几个特点:
cb 运行时可以访问到 Functional 组件的内部变量(包含通过 useState 生成的任何 state 和 setState);
cb 是否执行取决于依赖数组里的依赖项是否发生变化。如果没有依赖数组,每次 render 后都会调用 cb。如果依赖数组为[],仅在第一次 render 后调用;
通过将副作用相关代码放在 useEffect 的 cb 中,并在 cb 返回的函数里移除副作用,我们可以在一个 useEffect 中实现任何想要的生命周期控制:
React Hooks 的优点
通过 Hooks 我们可以对 state 逻辑进行良好的封装,轻松做到隔离和复用,优点主要体现在:
使用组合方式更优雅:不同于 render props 或高阶组件等的模式,hooks 不会在组件树中引入不必要的嵌套,也不会受到 mixins 的负面影响;
更少的代码量:一个 useEffect 执行单一职责,可以干掉生命周期函数中的重复代码。避免将同一职责代码分拆在几个生命周期函数中,更好的复用能力可以帮助优秀的开发者最大限度降低代码量;
点分享 点点赞 点在看