乐趣区

关于react.js:思考-React-Hooks-的设计哲学

背景

React Hooks 曾经进去有段时间了,很多小伙伴或多或少都用过。

明天呢,咱们就回头再看一下这个货色,思考一下,这个货色为什么会呈现,它解决了什么问题,以及背地的设计理念。

注释

如果你有 Hooks 的应用教训,能够思考一下这两个问题:

  1. Hooks 为什么会产生
  2. Hooks 解决了什么问题

咱们先次要围绕这两点展开讨论。

1. Hooks 为什么会产生

在正式开始这个话题前,咱们先回顾一下 react 的 发家史.

2013 年 5 月 13 号,在 JS Conf 上公布了第一个版本0.3.0.

我第一次接触 React 是在 2015 年,对 createClass 语法历历在目:

createClass 是过后第一种用于创立 React 组件的语法, 因为过后 Javascript 还没有成形的 Class 体系。

这种状况在 2015 年 1 月 17 号失去了扭转。

这时候,ES6 正式公布,反对 Class 语法。

这时候面临一个抉择:

持续用自家的 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 = {};
}

到这一步,曾经解决了两个令人好受的点:

  1. super
  2. bind

曾经足够 OK 了,是吧。

其实还不够。

咱们在编写 react 利用的时候,难以避免的一件事就是:拆分 react 组件。

把一个简单的 UI 视图 拆分 成不同的模块,而后 组合 在一起。

这也是 react 自身推崇的理念:万物皆可是组件。

这种设计很棒棒,但仍旧有很多问题。

我认为次要是亮点:

  1. 组件内逻辑的割裂
  2. 逻辑复用艰难

1. 先说 逻辑上的割裂

基于生命周期的设计,使得咱们常常写出 逻辑割裂 的代码:

同样的逻辑,咱们须要在不同的生命周期中去实现。

在一个大型的 app 中,相似的逻辑会有很多,掺杂在一起。

前人要去批改的时候,不得不应用 上下左右重复横跳之术, 令人非常苦楚。

2. 逻辑复用艰难

咱们都晓得,react 利用其实是由一些列 UI 套件组合而成的,这些套件有些有状态,有些没有状态。

把这些组件组合在一起,解决好复用,其实是有肯定艰难的。

比方,假如在另外一个组件,有和上图类似的逻辑,怎么办呢?

Copy & Paste 显然是能够的,但却不是最优雅的。

React 自身并不提供解决方案,然而 机智的网友们 逐步摸索出了一些改善这个问题的办法:

  1. High Order Components
  2. Render Props

High Order Components 为例,看一下最简的例子

为组件都退出一个 data 属性,而后返回这个加强的组件:

逻辑并不简单。

回到咱们最后的那个例子,当初要把这部分逻辑抽离进去,实现一个WithRepos 高阶办法:

应用的时候,包裹一下就能够了:

Render Props 也是同样的目标,不作赘述,可参考:Render Props

这两种做法,都能够改善逻辑复用的窘境,但同时又引入了新的问题。

还是以高级组件为例,比方咱们对一个组件要退出多个加强性能,不言而喻,代码就变成了:

export default withA(
  withB (
     withC (
        withD (Component)
     )
  )
)

Render Props 也一样,这两种模式都会限度你的组件构造,随着性能的减少,包裹的层数越来越多,陷入所谓的 wrapper hell之中。

这种状况并不是咱们想要的。

写到这里,咱们进行一个简略的总结,整顿一下遇到的问题:

  1. 咱们应用 Class 语法来生成组件,super 语法很烦,然而能够跳过。
  2. this 让人懵逼。
  3. 基于生命周期的设计,容易造成逻辑上的割裂,不容易保护。
  4. React 没有当前好的模式来解决逻辑复用问题。

所以,迫切需要一种 新的模式 来解决以上这些问题。

现实中,这种模式要具备以下特点:

  1. 简略好用
  2. 足够灵便
  3. 不便组合
  4. 扩展性强

那么,这种 新的模式 该如何设计呢?

此处援用一下 John Carmack 的话:

而且,Javascript 自身对 function 也是天生敌对。

所以,这时候要做的就是:

  • 摈弃 React.Component
  • 拥抱 function

在这个背景下,Hooks 应运而生。

2. Hooks 解决了什么问题

拥抱 Function, 背后就有三座大山须要解决:

  1. 组件 State
  2. 生命周期
  3. 逻辑复用难题

2.1 State

State Hook 的规范模式是返回一个元组,蕴含两个元素:

应用起来也十分的简略:

至此,有了 state hook, function 就具备了根底的状态治理能力:

  1. 组件 State ✅
  2. 生命周期
  3. 逻辑复用难题

2.2 Lifecyles

这一步,咱们先遗记传统 Class Component 的生命周期办法,想一下,如何在 Function 中实现相似的能力。

咱们要用这样的能力去实现,比方:

  1. 状态变更
  2. 数据获取
  3. 等等

基于这样的思考,useEffect 问世了。

useEffect 赋予了 Function 在组件外部执行副作用的能力。

就模式而言,Effect Hook 承受两个参数:

  1. 一个 function.
  2. 一个可选的 array.

简略的例子:

当 username 变动时,就批改 document title.

⚠️ 留神

有时候,你兴许会不经意间把 Effect 写成一个 async 函数:

强烈建议你不要这样做。

useEffect 约定:

Effect 函数 要么没有返回值 要么返回一个 Cleanup 函数

而这里 async 函数会隐式地返回一个 Promise,间接违反了这一约定,会造成 不可预测的后果

至此,Function 组件也有了应该具备的生命周期办法。

  1. 组件 State ✅
  2. 生命周期 ✅
  3. 逻辑复用难题

只剩最初一个课题:逻辑复用。

2.3 Sharing Non-Visual Logic

传统而言,咱们把页面拆分成一个个 UI 组件,而后把这个 UI 组件组合起来。这种状况最终也不可避免的诞生了 HOC & Render Props 等模式来改善逻辑复用问题。

你可能会想,React Hooks 可能会有新的解决办法。

方法确实是有,它就是Custom Hooks.

你能够把须要复用的逻辑抽成一个个独自的Custom Hook, 在须要用的中央应用。

举个例子:

把须要复用的逻辑抽离:

在须要用到的中央应用:

这样,咱们就轻松而又天然的实现了逻辑的复用。

  1. 组件 State ✅
  2. 生命周期 ✅
  3. 逻辑复用难题 ✅

至此,三个难题得以解决。

Hooks 的价值所在

回头咱们再看这个问题,其实从始至终,要解决的问题只有一个:

晋升代码复用以及组合的能力。

顺带的,也肯定水平上晋升了组件的内聚性,缩小了保护老本:

相干的逻辑都在独自的一块,改需要的时候,不必须要施展上下左右重复横跳之术,提前了下班时间,多好。

结尾

知其然,也要知其所以然。

咱们在本人平时的搬砖静止中,也要思考本人的代码是否具备一下能力:

  1. 简略好用
  2. 足够灵便
  3. 不便组合
  4. 扩展性强

不坑本人,也不坑他人,早点上班。

好了,别的就不扯了,心愿这篇文章能给你一些启发和思考。

满腹经纶,文章若有谬误,欢送留言之正。

退出移动版