blog icon indicating copy to clipboard operation
blog copied to clipboard

React进阶系列之Why Hooks?

Open campcc opened this issue 4 years ago • 0 comments

whyhooks.png

本篇我们来深入一个问题,Why React Hooks?

上一篇我们几乎花了通篇的文字阐述了 Hooks 的设计理念,最后的结论是 函数组件从设计思想上来看,更加契合 React 的理念,这可以作为上述问题的一个答案,除此之外,还有哪些理由可以作为我们去使用 React Hooks 的原因吗?

Why Hooks?

以 “Why xxx” 开头的问题,往往都没有标准答案,但会有一些关键的“点”,除了设计理念层面,还有以下原因,

  • 告别难以理解的 Class
  • 解决业务逻辑难以拆分的问题
  • 使状态逻辑复用变得简单可行

接下来我们就上述三个点展开来看一下,Why Hooks?

告别难以理解的 Class:把握 Class 的两大痛点

类组件的两大痛点是什么?this和生命周期

对于生命周期,大家应该都了解,经过三次版本的更迭,其中有不少学习和理解成本。我们重点来说说 this,以下面代码为例,我们不用关心组件具体的逻辑,就看 changeText 这个方法:它是 button 按钮的事件监听函数。当我点击 button 按钮时,希望它能够帮我修改状态,但事实是,点击发生后,程序会报错。原因很简单,changeText 里并不能拿到组件实例的 this。

class Example extends Component {
  state = {
    text: ""
  };
  changeText() {
    // 这里会报错
    this.setState({ text: 'Hello'});
  }
  render() {
    return <button onClick={this.changeText}>{this.state.text}</button>
  }
}

为了解决 this 不符合预期的问题,前端几乎也是各显神通,之前用 bind、现在推崇箭头函数。但不管什么招数, 本质上都是在用实践层面的约束来解决设计层面的问题。而 Hooks 基于函数组件,我们不需要关心 this,因为根本就没有 this,这在心智层面和理解上无疑是优于类组件的。

解决业务逻辑难以拆分的问题:Hooks可以实现更好的逻辑拆分

回忆一下在过去,我们是如何组织业务逻辑的?多数情况下应该都是先想清楚业务的需求是什么样的,然后将对应的业务逻辑拆分到不同的生命周期函数里面去,这意味着什么呢?意味着逻辑与生命周期耦合在一起了

在这样的前提下,我们发现生命周期函数往往背离了单一职责原则,一个生命周期函数里面往往需要做不只一件事,在稍复杂的 React 应用里,它们的体积往往很庞大,内部的逻辑也异常复杂,给阅读和维护带来很多麻烦,比如下面这个例子,

class Example extends Component {
  componentDidMount() {
    // 1. 这里发起异步调用
    // 2. 这里从 props 里获取某个数据,根据这个数据更新 DOM
    // 3. 这里设置一个订阅
    // ...
  }
  componentWillUnMount() {
    // 在这里卸载订阅
  }
  componentDidUpdate() {
    // 1. 在这里根据 DidMount 获取到的异步数据更新 DOM
    // 2. 这里从 props 里获取某个数据,根据这个数据更新 DOM
  }
}

这些事情之间看上去毫无关联,逻辑就像是被“打散”进生命周期里了一样 。比如,设置订阅和卸载订阅的逻辑,虽然它们在逻辑上是有强关联的,但是却只能被分散到不同的生命周期函数里去处理,这无论如何也不能算作是一个非常合理的设计

而在 Hooks 的帮助下,我们完全可以把这些繁杂的操作 按照逻辑上的关联拆分进不同的函数组件里: 我们可以有专门管理订阅的函数组件、专门处理 DOM 的函数组件、专门获取数据的函数组件等。Hooks 能够帮助我们 实现业务逻辑的聚合,避免复杂的组件和冗余的代码

Hooks使状态逻辑复用变得简单可行

React 在原生层面并没有为我们提供状态逻辑复用的姿势,所以在过去,我们靠的是 HOC(高阶组件)和 Render Props 这些组件设计模式,但这些设计模式并非万能,它们在实现逻辑复用的同时,也破坏着组件的结构,其中一个最常见的问题就是“嵌套地狱”现象。

而 Hooks 可以看作 React 为解决状态逻辑复用这个问题所提供的一个原生途径,现在我们可以通过自定义 Hooks 来解决状态复用的问题。

保持清醒:Hooks 并非万能

尽管 Hooks 有千般好,我们仍要保持清醒,认识到它的一些局限性,主要有以下几点,

  • Hooks 暂时还不能完全地为函数组件补齐类组件的能力 :比如 getSnapshotBeforeUpdate、componentDidCatch
  • “轻量”几乎是函数组件的基因,这可能会使它不能够很好地消化“复杂” :如果用函数组件来解决复杂的业务问题,逻辑的拆分和组织会是一个很大的挑战,耦合和内聚的边界,有时候真的很难把握, 函数组件给了我们一定程度的自由,却也对开发者的水平提出了更高的要求
  • Hooks 在使用层面有着严格的规则约束:如果不能牢记并践行 Hooks 的使用原则,对 Hooks 的关键原理没有扎实的把握,很容易把自己的 React 项目搞成大型车祸现场

相关文章

写在最后

本文首发于我的 博客,才疏学浅,难免有错误,文章有误之处还望不吝指正!

如果有疑问或者发现错误,可以在评论区进行提问和勘误,

如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。

campcc avatar Aug 24 '21 11:08 campcc