React Hooks 已经出来有段时间了, 不少小伙伴或多或少都用过。javascript
今天呢,咱们就回头再看一下这个东西,思考一下,这个东西为何会出现,它解决了什么问题, 以及背后的设计理念。html
若是你有 Hooks 的使用经验, 能够思考一下这两个问题:java
咱们先主要围绕这两点展开讨论。react
在正式开始这个话题前, 咱们先回顾一下react的发家史
.app
2013年5月13号, 在JS Conf 上发布了第一个版本0.3.0
.async
我第一次接触React 是在2015年, 对createClass语法记忆犹新:ide
createClass
是当时第一种用于建立 React 组件的语法, 由于当时Javascript 尚未成形的 Class 体系。函数
这种状况在2015年1月17号获得了改变。post
这时候, ES6 正式发布,支持 Class 语法。ui
这时候面临一个选择:
继续用自家的 createClass
呢 仍是使用新的 ES6 Class
?
毕竟 createClass 又不是不能用, 并且用着还挺顺手。
最后, React 仍是选择了拥抱新趋势, 使用ES6 Class。
并在 React 0.13.1 Beta1
版本, 开始支持使用原生Javasciprt Class语法。 我找到了以下说明:
大意就是: 咱们并不想本身单独搞一套, 你们习惯怎么用, 咱们就怎么搞。
基于这个变化, React Class 组件就变成了咱们以前常常见到的这样:
是否是很熟悉。
生命周期方法和以前保持一致
,变化的是组件初始化
的部分。
从本来的getInitinalState
变成了constructor
。
经历过这个阶段的小伙伴确定对如下代码段很是熟悉:
constructor(props) { super(props); // 🤮🤮🤮 this.state = {}; this.xxx = this.xxx.bind(this); // 😢😢😢 }
在组件初始化的时候, 必须手动super(props)
一下, 至于为何这么作, 本文不作讨论, 有兴趣的能够看一下这篇译文: 为何要写Super(props)。
Class Fields
也容许咱们跳过这一步:
class xxx extends React.Component { state = {}; }
到这一步, 已经解决了两个使人难受的点:
已经足够OK了, 是吧。
其实还不够。
咱们在编写react 应用的时候, 难以免的一件事就是: 拆分react 组件。
把一个复杂的UI视图拆分
成不一样的模块, 而后组合
在一块儿。
这也是 react 自己推崇的理念: 万物皆但是组件。
这种设计很棒棒, 但依旧有不少问题。
我认为主要是亮点:
组件内逻辑的割裂
逻辑复用困难
逻辑上的割裂
:基于生命周期的设计, 使得咱们常常写出逻辑割裂
的代码:
一样的逻辑, 咱们须要在不一样的生命周期中去实现。
在一个大型的app 中, 相似的逻辑会有不少, 掺杂在一块儿。
后人要去修改的时候, 不得不使用上下左右反复横跳之术
, 使人十分痛苦。
逻辑复用困难
咱们都知道, react 应用实际上是由一些列 UI 套件组合而成的, 这些套件有些有状态, 有些没有状态。
把这些组件组合在一块儿,处理好复用, 实际上是有必定困难的。
好比,假设在另一个组件,有和上图类似的逻辑, 怎么办呢?
Copy & Paste
显然是能够的, 但却不是最优雅的。
React 自己并不提供解决方案,可是机智的网友们
逐渐摸索出了一些改善这个问题的方法:
High Order Components
Render Props
以High Order Components
为例, 看一下最简的例子
为组件都加入一个data属性, 而后返回这个加强的组件:
逻辑并不复杂。
回到咱们最初的那个例子, 如今要把这部分逻辑抽离出来, 实现一个WithRepos
高阶方法:
使用的时候, 包裹一下就能够了:
Render Props 也是一样的目的, 不做赘述, 可参考:Render Props
这两种作法, 均可以改善逻辑复用的困境,但同时又引入了新的问题。
仍是以高级组件为例, 好比咱们对一个组件要加入多个加强功能,显而易见, 代码就变成了:
export default withA( withB ( withC ( withD ( Component ) ) ) )
Render Props 也同样, 这两种模式都会限制你的组件结构,随着功能的增长, 包裹的层数愈来愈多,陷入所谓的 wrapper hell
之中。
这种状况并非咱们想要的。
写到这里, 咱们进行一个简单的总结, 整理一下遇到的问题:
因此, 迫切须要一种新的模式
来解决以上这些问题。
理想中, 这种模式要具有如下特色:
那么, 这种新的模式
该如何设计呢?
此处引用一下John Carmack的话:
并且, Javascript 自己对 function 也是天生友好。
因此, 这时候要作的就是:
在这个背景下, Hooks 应运而生。
拥抱 Function, 面前就有三座大山须要解决:
State Hook 的标准形式是返回一个元组, 包含两个元素:
使用起来也很是的简单:
至此,有了state hook, function 就具有了基础的状态管理能力:
这一步, 咱们先忘记传统 Class Component 的生命周期方法, 想一下, 如何在 Function 中实现相似的能力。
咱们要用这样的能力去实现,好比:
基于这样的思考, useEffect 问世了。
useEffect 赋予了Function 在组件内部执行反作用的能力。
就形式而言, Effect Hook 接受两个参数:
简单的例子:
当 username 变化时, 就修改document title.
⚠️ 注意
有时候,你也许会不经意间把 Effect 写成一个 async 函数:
强烈建议你不要这样作。
useEffect 约定:
Effect 函数要么没有返回值
,要么返回一个 Cleanup 函数
。
而这里 async 函数会隐式地返回一个 Promise,直接违反了这一约定,会形成不可预测的结果
。
至此, Function 组件也有了应该具有的生命周期方法。
只剩最后一个课题: 逻辑复用。
传统而言, 咱们把页面拆分红一个个UI组件, 而后把这个UI组件组合起来。 这种状况最终也不可避免的诞生了 HOC & Render Props 等模式来改善逻辑复用问题。
你可能会想, React Hooks 可能会有新的解决办法。
办法的确是有, 它就是Custom Hooks
.
你能够把须要复用的逻辑抽成一个个单独的Custom Hook
, 在须要用的地方使用。
举个例子:
把须要复用的逻辑抽离:
在须要用到的地方使用:
这样, 咱们就轻松而又天然的实现了逻辑的复用。
至此, 三个难题得以解决。
回头咱们再看这个问题, 其实从始至终, 要解决的问题只有一个:
提高代码复用以及组合的能力。
顺带的, 也必定程度上提高了组件的内聚性, 减小了维护成本:
相关的逻辑都在单独的一块, 改需求的时候,不用须要施展上下左右反复横跳之术,提前了下班时间, 多好。
知其然,也要知其因此然。
咱们在本身平时的搬砖运动中, 也要考虑本身的代码是否具有一下能力:
不坑本身, 也不坑别人, 早点下班。
好了, 别的就不扯了, 但愿这篇文章能给你一些启发和思考。
才疏学浅, 文章如有错误, 欢迎留言之正。