1. Dotnet9首页
  2. 大前端
  3. 前端语言
  4. React

React Hooks 原理与最佳实践

1. 前言

React Hooks 是 React 16.8 引入的新特性,允许我们在不使用 Class 的前提下使用 state 和其他特性。React Hooks 要解决的问题是状态共享,是继 render-props 和 higher-order components 之后的第三种状态逻辑复用方案,不会产生 JSX 嵌套地狱问题。

2. 状态逻辑复用

一般来说,组件是 UI 和逻辑,但是逻辑这一层面却很难复用。对用户而言,组件就像一个黑盒,我们应该拿来即用。但当组件的样式或者结构不满足需求的时候,我们只能去重新实现这个组件。

React Hooks 原理与最佳实践

在我们开发 React 应用的时候,经常会遇到类似下面这种场景,你可能会有两个疑问:

  1. Loading 是否可以复用?
  2. Loading 该怎么复用?
React Hooks 原理与最佳实践

这几个例子都指向了同一个问题,那就是如何实现组件的逻辑复用?

2.1 render props

将函数作为 props 传给父组件,父组件中的状态共享,通过参数传给函数,实现渲染,这就是 render props。使用 render prop 的库有 React Router、Downshift 以及 Formik。以下面这个 Toggle 组件为例子,我们一般可以这样用:

React Hooks 原理与最佳实践

可以看到,控制 Modal 组件是否展示的状态被提取到了 Toggle 组件中,这个 Toggle 组件还可以拿来多次复用到其他组件里面。那么这个 Toggle 是怎么实现的呢?看到实现后你就会理解 render props 的原理

React Hooks 原理与最佳实践

关于 render props 的更多内容可以参考 React 中文网的相关章节:Render Props

2.2 higher-order components

higher-order components 一般简称 hoc,中文翻译为高阶组件。从名字上就可以看出来,高阶组件肯定和高阶函数有什么千丝万缕的关系。高阶组件的本质是一个高阶函数,它接收一个组件,返回一个新的组件。在这个新的组件中的状态共享,通过 props 传给原来的组件。以刚刚那个 Toggle 组件为例子,高阶组件同样可以被多次复用,常常可以配合装饰器一起使用。

React Hooks 原理与最佳实践

高阶组件的实现和 render props 也不太一样,主要是一个高阶函数。

React Hooks 原理与最佳实践

2.3 render props 和高阶组件的弊端

不管是 render props 还是高阶组件,他们要做的都是实现状态逻辑的复用,可这俩是完美的解决方案吗?考虑一下,如果我们依赖了多个需要复用的状态逻辑的时候,该怎么写呢?以 render props 为例:

React Hooks 原理与最佳实践

看看这个代码,你有没有一种似曾相识的感觉?这一天,我们终于想起被“回调地狱”支配的恐惧。不得不再次祭出这张图了。

React Hooks 原理与最佳实践

同样地,高阶组件也会有这个问题,但由于装饰器的简洁性,没有 render props 看起来那么可怕。除此之外,他们俩还有另一个问题,那就是组件嵌套过深之后,会给调试带来很大的麻烦。这个是 render props 中组件嵌套在 React 开发者工具中的表现。

React Hooks 原理与最佳实践

对于高阶组件来说,如果你没有对组件手动设置 name/displayName,就会遇到更严重的问题,那就是一个个匿名组件嵌套。毕竟上面 render props 的嵌套至少能知道组件名。

React Hooks 原理与最佳实践

社区里面也已经有很多解决 render props 嵌套的方案,其中 Epitath 提供了一种以 generator 的方法来解决嵌套问题,利用 generator 实现了伪同步代码。

React Hooks 原理与最佳实践

更多细节可以参考黄子毅的这篇文章:精读《Epitath 源码 – renderProps 新用法》

2.4 React Hooks

React Hooks 则可以完美解决上面的嵌套问题,它拥有下面这几个特性。

  1. 多个状态不会产生嵌套,写法还是平铺的
  2. 允许函数组件使用 state 和部分生命周期
  3. 更容易将组件的 UI 与状态分离
React Hooks 原理与最佳实践

上面是一个结合了 useState 和 useEffect 两个 hook 方法的例子,主要是在 resize 事件触发时获取到当前的 window.innerWidth。这个 useWindowWidth 方法可以拿来在多个地方使用。常用的 Hook 方法如下:

React Hooks 原理与最佳实践

3. useState & useRef

useState 是 React Hooks 中很基本的一个 API,它的用法主要有这几种:

  1. useState 接收一个初始值,返回一个数组,数组里面分别是当前值和修改这个值的方法(类似 state 和 setState)。
  2. useState 接收一个函数,返回一个数组。
  3. setCount 可以接收新值,也可以接收一个返回新值的函数。
const [ count1, setCount1 ] = useState(0);const [ count2, setCount2 ] = useState(() => 0);setCount1(1); // 修改 state

3.1 和 class state 的区别

虽然函数组件也有了 state,但是 function state 和 class state 还是有一些差异:

  1. function state 的粒度更细,class state 过于无脑。
  2. function state 保存的是快照,class state 保存的是最新值。
  3. 引用类型的情况下,class state 不需要传入新的引用,而 function state 必须保证是个新的引用。

3.2 快照(闭包) vs 最新值(引用)

在开始前,先抛出这么一个问题。在 1s 内频繁点击10次按钮,下面代码的执行表现是什么?

本文较详细、较长,可阅读原文查看

原文出处:微信公众号【尹光耀 前端工匠】

原文链接:https://mp.weixin.qq.com/s/l5axhu1D3CwUxpPVO9oo5w

本文观点不代表Dotnet9立场,转载请联系原作者。

发表评论

登录后才能评论