小卢碎碎念之 React Hooks

1,327 阅读7分钟

此文主要是个人在使用了 react hooks 一段时间后, 对于 react hooks 的理解,也是一个碎碎念。各位大佬如果觉得有什么问题可以提出来讨论讨论,也接受批评哦~

是什么

本质上看,好像每一个Hooks都是一个函数吼。

但,既然是函数,又为什么要另起一个名字(概念)?

所以, 写/用法上肯定会与普通函数有些区别。

与普通函数的异同

如果说, 我们现在需要实现一个 TodoList 的增删需求。我们可以函数, 和hooks 分别实现, 然后对比差异。

实现简单TodoList

利用闭包

利用闭包,我们可以大概实现如下的逻辑块:

使用Hooks

看上面 闭包hooks 分别实现的功能, 貌似也没啥区别吼。

那就继续往下, 看实际应用到 函数式组件 中的样子。

闭包逻辑在函数式组件中的使用

Hooks 在函数式组件中的使用

从上两个图可以发现。returnjsx 部分完全相同, 可以舍去不看。注意力主要放在除了 return 之外的地方(也就是 return 以上的函数体部分)

首先我们看 为了使用闭包逻辑,而对 组件进行调整的部分。

因为上图是结果, 一下子看可能有些同学有点蒙, 我将流程拆解下。

第一步、

因为要在组件初始化的时候去创建TodoList逻辑, 并且将其对外暴露的api进行保存, 所以, 我们可以有如下代码。(一个创建, 一个保存)。

第二步、

接着,我们就要把数据交给视图去渲染, 把逻辑交给视图来触发了, 该怎么写呢, 是这样吗?

如果这样写,有些同学可能会发现, 怎么点击add 按钮咋子没得效果嘞。看逻辑没得错啊,用闭包函数提供出来的数据,也用它提供出来的api, 对数据进行增加, 但是为啥没得效果嘞!?

介里, 咱就得提一下关于 函数式组件的更新条件。

  1. 组件内部使用useState返回的setState 函数引起的state 变更,则会去引起当前组件的 rerender ;
  2. 组件外部传入的 props 变更,也会引起组件的 rerender;
  3. 第三个就是 useContext 了,当前组件使用的上下文中, 只要有一条数据变更, 就会引起该组件的 rerender (context 还是得拆分的细一点 =.=)

更新条件介绍完毕, 回到主题, 好像我们更改闭包函数内部数据, 一个条件都没和上面三个吻合的, 所以, 我们得想个办法,每次更新了数据后,让函数式组件去 rerender

而这里就选用的第一种, 更新state来引起组件的rerender

说干就干, 首先,在组件内部增加一个 useState 来记录 list , 然后在每次对闭包函数内部的 list 进行增、删后, 都去获取最新值,并更新到视图上。

写完是这个样子啦, 然后去运行一下。。 。 没得问题, 美滋滋, 打卡下班,多么充实的一天~~~

直至N天后, 某个同事小吴要用到这个功能, 听着你给他讲, 要这么这么用, 要记录什么什么 state, 小吴怕是都要按不住手中的刀了。所以, 为了我们的生命安全, 我们要尽量的站在小吴的角度,去考虑如何将一个功能更加内聚, 对外部的使用要求更小

此时, 也该 react hooks 闪亮登场了(废话了这么多, 干)

去除 return 部分, 我们发现我们要用 TodoList 的逻辑, 只需要增加以下一行

有点蒙?我再把 useTodoList 的代码拿过来

还有在组件中的使用

怎么样, 是不是很简洁。哈哈哈哈。

接下来我们大概走一下这个hooks 的逻辑。

useTodoList 内部, 有记录一个 list 。以及有两个函数, 分别对list进行增加和删除。最后将 list,增加和删除逻辑return出去。然后外部组件中又去调用这增加删除逻辑, 修改了 hooks 中的数据, 然又引起外部组件的rerender

而此时, 则又涉及到了一个更新条件, 上面讲的三条函数式组件的更新条件, 此时也就得多加一条了

​ 4. 函数式组件中所用到的 hooks 中, 如果更新了 state, 则会依次由上往下(hooks 书写顺序),由内往外的触发 hooks函数式组件rerender

由此条件, 我们才能在 hooks 中的数据变更的时候引起外部的视图更新。

其实, hooks 个人感觉跟 函数式组件一样, 只不过hooks 抛出的是API,是一些逻辑处理函数。 而函数式组件抛出的是 Fiber Node, 是将要渲染到视图上的东西。

另外的, 说起hooks 和 闭包函数差异,hooks 内部可以用其他hooks, 以及通过 useEffect 来实现的一些 生命周期之类, 闭包函数则不行, 闭包函数要强行做, 还得外部去调用,那样对外部的要求又高了, 小吴的刀怕是两个人都按不住了。

所以,对于这个主题, hooks 与普通函数的异同, 总的来说

  • hooks呢, 可以使用 其他的 hooks , 用法跟函数式组件 一致, 只不过return的不是JSX, 而是一些 API 或者数据。当然这里讲的只是输出, 还有输入的, 如 useTodoList(['666']) 这种的

  • 对于函数, 我们可以利用闭包现象,来写一段内聚的逻辑,然后暴露一些API给外部来操控内部的数据和逻辑。

但是 hooks 要求相对于闭包函数较高, 得运行在 react 环境中, 或者 vue3?(vue3 的 Composition API 对应 react 的话,应该就是 react hooks)vue3的还不太清楚 hooks 写法是否和 react 的一致,如果一致的话, 是不是一个 hooks 就可以在两个框架中共用?

插一句

接下来基本都是我的猜想, 没得实践过,大家可以来讨论讨论。

可以做什么

上面一点有讲到

hooks 个人感觉跟 函数式组件一样, 只不过hooks 抛出的是API,是一段逻辑。 而函数式组件抛出的是 Fiber Node, 是将要渲染到视图上的东西。

所以, 针对一些比较复杂且公共的逻辑, 我们就可以将逻辑封装成一个自定义hooks。供不同UI来共享该逻辑。

如果激进一点, 我们可以将 视图 和 逻辑完全的分离, 一个组件分成两个, 一个逻辑的 hooks , 一个视图的 template, 但是感觉这样写好像变成了Vue, 干, 具体还是得去实践,看有啥优缺点。

单元测试

单元测试分 功能测试 和 UI测试。

功能测试对应 Hooks 的测试, 测试该 hooks 的逻辑是否正常。

UI测试对应 template 的测试, 测试输入固定数据, 视图有没有正确的渲染出来。

由 数据驱动视图( UI = render(State) ) 这一理念,我们对于一个功能的测试, 完全可以将重心放在 hooks 上, 对于 dom 的测试, 只需要测试数据有无正确渲染就行。而不需要 各种获取 dom,触发这个那个事件,事件里又去触发对应逻辑。

抽象来看, 我们测试一块地方的功能, 分两层, 一层视图, 一层逻辑。 我们与视图的交互,目的就是为了去触发某个逻辑。 视图只是用户与逻辑之间的一个媒介而已。所以测试的话, 我觉得可以绕过繁琐的视图层操作。直接测试 hooks 功能。而视图则是侧重在固定的数据, 是否有固定的视觉呈现。(这个的前置条件就得是 视图与逻辑的分离)

纯JS/TS业务逻辑共用

如果说, 我们有一个产品, 分为 PC端、H5端、RN端、Taro小程序端。 那是否能让这些不同端都共享同一套业务逻辑代码?

写在最后

线上代码链接点击这里

以上基本全是本人在使用了 hooks 一段时间后的, 对于hooks 的理解, 以及对一些实践方式的猜想。

也欢迎各位大佬一起来参与讨论。