Umi Hooks - 助力拥抱 React Hooks

这是蚂蚁金服内部技术分享的文字稿,本次分享主要介绍了为何要用 Hooks?以及如何使用 Umi Hooks 提效?

Umi Hooks http://github.com/umijs/hooksreact

开场

image-20200116191741370

你们好,我叫尽龙,来自体验技术部。社区名称叫 brickspert,砖家的意思。git

自从 React 推出 React Hooks 后,就开始尝试使用 Hooks,并逐渐喜欢上了它。目前,几乎 100% 的组件都是使用 Hooks 开发。通过大半年的实践,在 Hooks 使用方面沉淀了一些经验,很高兴今天有机会能分享给你们。程序员

image-20200116191759286

在分享开始以前,我想了解下:“有多少同窗目前已经在项目中大量使用 Hooks 了?”github

嗯嗯,谢谢。看举手的同窗,大概一半一半吧。不要紧,听完今天的分享,我相信你必定有兴趣尝试下 Hooks 的。面试

React Hooks 是 react v16.8 的一个新特性,很佩服这么重磅的功能,在一个小版本中发布,说明 React 团队有足够的信心向上兼容。redux

Why Hooks?

image-20200116191817331

为何要放弃 Class,转用 Hooks 呢?在内部外部有不少争论,包括知乎也有相似提问。咱们也难免俗套的要对比下 Class 和 Hooks 了。固然为了保证今天的分享效果,我确定会偏向 Hooks 的(哈哈哈哈)。c#

image-20200116192324872

Class 学习成本高

Class 学习成本很高。首当其中的就是生命周期,多,太多了。不只多,还会变!React v15 和 v16 就不同。下面是我在网上随便找的一张图。网络

image-a5b927b35025

这个是 React v15 的生命周期,你都掌握了吗?你知道 v16 有什么变化吗?闭包

以前不管你去哪里面试,基本都会有几个必问问题:异步

  • 讲讲 React 生命周期?React v15 和 React v16 生命周期有啥变化?
  • 如何优化 Class 组件?shouldComponentUpdate 是作什么的?如何用?
  • 通常在哪一个生命周期发送网络请求?为何?
  • ......

生命周期最重要,可是有很高的学习成本,须要大量实践才能积累足够的经验。固然,这几个问题回答很差,百分之八十以上的概率会挂掉。

固然不止是生命周期,this 也是一个很大的问题。你有没有在组件写不少 bind?或者全部的函数都用箭头函数定义?

this.someFunction = this.someFunction.bind(this);

// 或
someFunction = ()=>{}

为何要这样写呢?若是不写会有什么问题?哎呦,又多了一个面试题,你会吗?

Hooks 学习成本低

对比 Class,Hooks 的学习成本可就过低了!掌握了 useState 和 useEffect,80% 的事情就搞定了。

image-7cbf7879e7cf

Class 业务逻辑分散

Class 业务逻辑分散,实现一个功能,我要写在不一样的生命周期里面,不聚合~

好比,若是你有个定时器,你必定要在 componentWillUnMount 去卸载。

image-67b7d915f6af

再好比,咱们要写一个请求用户信息的组件,当userId 变化时,要从新发起请求。咱们就要在两个生命中期中写请求的逻辑。

image-4059e72aa129

相信上面的逻辑,你们也是常常会写的吧。

奥奥,sorry,上面的 componentWillReceiveProps 已经被废弃了,咱们应该用 componentDidUpdate 来代替。

“咦,这是为啥呢?好好的为何要废弃,不让这么用了?”

又来一个面试题!你知道答案吗?

Hooks 业务逻辑聚合

而 Hooks 的业务逻辑就很是聚合了。上面的两个例子,改为 Hooks 你会写吗?

image-3780dc60b735

image-88e9ba8a7add

简直不要太简单!香啊!我能够提早下班了。

Class 逻辑复用困难

说到逻辑复用,不少同窗会说 Class 的 Render Props 和 HOC(高阶组件)能够作逻辑复用!那咱们看看 Class 的逻辑复用有多么的惨不忍睹。

首先咱们看看 Render Props。

首先咱们想复用监听 window size 变化的逻辑,开开心心的写了下面的代码。

image-f9273eefa2ef

而后,我又想复用监听鼠标位置的逻辑,我只能这么写了。

image-d60b6492b570

到这里你应该看到了问题所在。这简直就是地狱!我不忍心复用其它逻辑了。

咱们放过 Render Props,来看看 HOC 吧。

若是你要问什么是 HOC,那我不得不推荐个人另一篇文章《 助你彻底理解React高阶组件(Higher-Order Components)》。

哪怕你不知道 HOC 是啥,你也必定用过。好比 redux 的 connect。

image-20200116200932301

上面的代码,我用了三个 HOC,分别是 redux 的 connect,react-intl 的 injectIntl,以及 AntD 的 Form.create()。

这是一个很是常见的用法。若是你光看代码,大概已经懵圈了。“我是谁?我在哪?我要干什么?”

这会我仿佛听见 HOC 在说:“我不只让你看不懂我,我还很容易出各类问题。”

是的,HOC 很容易出问题。你们都往组件的 props 上面挂属性,万一有个重名的,那就只能说一句“很差意思,GG思密达”!

Hooks 逻辑复用简单

Hooks 来了,它表示,我要一个打五个!Render Props 和 HOC 联合起来也被我秒杀!

image-5a6f5d648ca9

Hooks 表示,来十个,来一百个我也能打。

Hooks 最强的能力就是逻辑复用了,这是我最最最爱的能力了。

Hooks 会产生不少闭包问题

是的,我也不偏袒 Hooks,因为 React Hooks 的机制,若是用法不正确,会致使各类奇怪的闭包问题。

若是你要问 React Hooks 的机制是什么的话,我又要给你推荐一篇我以前写的文章了:《 React Hooks 原理》。

那面对这个问题,怎么解呢?说实话,我也没有很好的解决办法。

可是,这可能也有好处。若是碰到想不明白的问题,那 99% 是因为闭包致使的,咱们有很肯定的方向去排查问题。

image-4636b47be14f

记住这句话,你能够少走不少弯路。

Show Case

image-20200116203233594

固然,说再多,吹再好,也没多大用。我上面讲的 Class 和 Hooks 的优缺点,网上的也有不少人讲,你们也确定都看过。

用程序员的交流方式,就是“Talk is cheap,Show me the code.”。

亮剑吧!

接下来,我会用一个例子,让你折服,拜倒在 Hooks 的石榴裙下。若是你不服,我们单独撕~

网络请求组件实现

image-20200116214124140

接下来,咱们来实现一个最最最多见的组件。该组件接收 userId,而后发起网络请求,得到用户信息。

说白了,就是最简单的发起网络请求的组件。咱们先用 Class 来实现看看。

image-20200116214639300

这段代码,是最简单的网络请求。

  • 定义一个 username 状态。
  • componentDidMount 的时候发起网络请求。
  • 网络请求结束,更新 username。

美滋滋。可是少了点东西。网络请求,咱们确定要维护一个 loading 状态,保证用户体验比较好。

那咱们加上吧。

image-20200116214918755

这张图,咱们增长了 loading 状态,在网络请求发起前,置为 true,在网络请求结束后,置为 false。

美滋滋。可是仍是少点东西。userId 变化后,我要从新发起网络请求吧。

咱们再加点代码吧。

image-20200116215101730

咱们增长了对 userId 变化的监听,若是 userId 变化后,从新发起请求。

此次稳了吧?

不不不,还不够。若是 userId 连续变化了 5 次,发送了 5 个网络请求,咱们要保证老是最后一次网络请求有效。也就是常常说的的“竞态处理”或者“时序控制”。

我加!加还不行吗!

image-20200116215409524

其实到这里,有些同窗已经懵了。“你说的时序控制,听着颇有道理,但我平时都没处理过这个问题,我看下你怎么实现的。”

确实,时序控制不算一个简单的问题,不少新手都不会解决这个问题。

稳了!到这里你以为稳了吧。

仍是年轻啊,小伙子。

image-20200116220003295

若是用上面的代码来玩,你可能会偶尔碰到上面的警告。这个警告是怎么形成的呢?我说一下你就明白了。下面四个步骤执行,必会报警告:

  1. 组件加载
  2. 发起网络请求
  3. 组件卸载
  4. 网络请求请求成功,触发 setState

看出问题了吗?组件已经卸载了,还去 setState,形成了内存溢出。

怎么解决呢?

image-20200116220311200

在组件卸载的时候,放弃最后一次请求。

到这里为止,咱们就完成了一个完美的网络请求。此次真结束了!

看下写了多少行代码。

image-20200116220531399

除去空格,咱们写了 38 行代码。实话说,38 行代码我能忍,可是这些逻辑我忍不了!回想下咱们处理了多少逻辑:

  • 网络请求
  • loading
  • userId 变化从新发起请求
  • 竞态处理
  • 组件卸载放弃网络请求

关键这些逻辑是没办法复用的!每一个项目可能有数十上百个组件会发网络请求,我就要写几十,几百遍这样的逻辑。想一想我都难受。

说实话,我在写项目的时候常常会偷懒。要不就不写 loading,要不就无论竞态,要不就无论最后的内存溢出警告。

你有没有和我同样呢?嘿嘿。

言归正传,接下来就邀请 Hooks 登场了。

image-20200116221123031

三下五除二,咱们用 Hooks 实现了刚才全部的逻辑。

image-20200116221212124

17 行!代码量减小了 50% 以上。好像还行!

可是,别忘了,Hooks 最重要的能力就是逻辑复用!这些逻辑咱们彻底能够封装起来!咱们把刚才的逻辑所有封装起来!

image-20200116221359407

useAsync 封装了刚才咱们说的全部功能,一行代码完成了网络请求。

最后整个组件会长这样。

image-20200116221605411

哇!我本身都佩服本身!简直了!美呆了,帅毙了,感受本身无敌了!提早完成工做,下班回家!

image-20200116221755193

经过这个例子,我想证实一个论点:“使用 Hooks 封装逻辑的能力,能够极大提升开发效率”。

Umi Hooks

这时候你确定要问,useAsync 在哪里?给我瞧瞧?

image-20200116221941442

useAsync

useAsync 在这里,快来瞧,快来看啦!

useAsync 是 Umi Hooks 库的核心 Hooks 之一,Umi Hooks 提供了大量提炼自业务的 Hooks。一行代码真的能够实现不少功能!

Umi Hooks 在这里在这里!你懂的~~

image-20200116222352082

固然,useAsync 不止包含上面说的功能,还支持“手动触发”执行,还支持“轮询”!

只要简单的配置一个 pollingInterval ,就能轮询发送请求了。快去试试啦

接下来咱们会介绍几个更牛的 Hooks 给你们认识!

useAntdTable

image-cc2f4b087aca

AntD 的 Table 组件,想必你们在项目中常常用到吧!除了刚才异步请求的全部逻辑外,你还得处理其它的逻辑。

image-20200116232857059

好比维护 page、pageSize、sorter、filter 的状态,还得考虑搜索条件变化后,重置 page 到第一页。这些逻辑光想一想就头疼了,别说写了。

如今一行代码就能够实现了!useAntdTable,封装了全部的逻辑,只要一行代码!如图上所示,你只要 ...tableProps,就能够了。这也许就是幸福的味道吧~

useLoadMore

加载更多的场景,好比下面动图的场景,想必你们在工做中都写过。

image-22fa47992b6f

这样一个加载更多的场景,咱们要维护多少状态?写多少行逻辑?原本我打算写个 Class 实现的例子贴出来的,可是我放弃了,由于太难了~~

随便想一想要处理的逻辑:

  • 第一次加载时候的 loading
  • 加载更多时候的 loading
  • 维护 page 和 pageSize
  • 网络请求
  • 是否是加载全了
  • 搜索条件变化后,重置到第一页。
  • .....

脑袋疼,真的脑袋疼。我会写,可是写起来真的好累。

还没完,通常产品同窗还会要求,上拉加载更多......

image-cf629db68ebc

这时候咱们还得监听滚动位置,若是快到底了,触发加载更多。脑袋更疼了!

image-20200116235013101

Umi Hooks 听到了你的求救,派出 useLoadMore 来拯救你了。一行代码就能够实现全部的功能!一个小时变一分钟,又能够早点下班了。

useDynamicList

image-20200116235455431

还有更好用的,好比 useDynamicList,下面的动态列表,一行代码搞定。

image-16556dfcf0e8

useBoolean

不只是上面讲到的各类复杂逻辑能够封装。简单的逻辑封装起来也是极其好用的,好比 Boolean 值的管理。

咱们通常控制 Modal,Popover 等显示隐藏的时候,都要维护一个 visible 状态。大概会是这样。

image-20200116235651552

这样的逻辑,你写过多少遍?没有几千也有几百吧!

image-20200116235850057

之后你就能够用 useBoolean 咯!

More

image-20200116235957789

不只是上面讲到的这些,咱们还有不少不少的 Hooks。

好比 useSearch,就封装了一般异步搜索场景的逻辑,好比 debounce。

好比 useVirtualList,就封装了虚拟列表的逻辑。

image-20200117000249183

好比 useMouse,封装了监听鼠标位置的逻辑。

好比 useKeyPress,封装了监听键盘按键的逻辑。

image-20200117000412951

30+ Hooks 供您选择,而且咱们仍然处于婴儿期,快速发展中。咱们的愿景就是:封装别人的逻辑,让别人无逻辑可写。

将来规划

image-20200117001130397

更多的 Hooks 开发

如上面所述,咱们如今还处于婴儿期,须要不断汲取能量,更多的 Hooks 正在路上!要实现“让别人无逻辑可写”的目标,还需继续奋斗。

更强大的 useRequest

image-20200117001209280

你们应该都听过 useSWR 吧?是 zeit 公司开发的一个专门作网络请求的 Hooks,提供了不少新颖的思路,给了咱们很是大的启发,github star 就像坐火箭同样。但在实际项目使用中,仍是会有不少地方不符合蚂蚁内部的体系。可是它给咱们很是大的启发,基于 swr 的思路,咱们能够实现更强大的 useRequest!图上的能力,咱们都要!

useRequest 目前已经处于内测期了,下个版本将会与你们见面!咱们的目标是:全部的网络请求,只用 useRequest 就够了!

Hooks 生态

目前社区上 Hooks 相关的基础教程、进阶教程、原理深刻、常见问题等文档都比较分散,咱们准备向 Hooks 生态发展,提供各式各样的文章。之后学习 Hooks,使用 Hooks,找 Umi Hooks 就对了。

固然,生态方面目前正在规划中,预计年后启动。

总结

image-20200117001711649

Umi Hooks,你值得拥有。

咱们目前处于发展阶段,欢迎你们一块儿共建。

你能够提 idea,咱们负责实现。

你能够提 issue,咱们负责改 bug。

你能够提 PR,将你封装的 Hooks 分享给你们,让更多人收益。

❤️期待您的参与。

相关文章
相关标签/搜索