大家好,我是若川。今天分享一篇关于「React Hooks」的好文。欢迎点击下方卡片关注我。
以下是正文~
回想下你入门Hooks
的过程,是不是经历过:
类比
ClassComponent
的生命周期,学习Hooks
的执行时机慢慢熟练以后,发现
Hooks
的执行时机和生命周期
又有些不同。比如componentWillReceiveProps
对应哪个Hooks
?感到困惑,去搜一些
Hooks
原理层面的文章阅读
作为一个API
,不该简简单单、可可爱爱的照着文档调用就行么,Hooks
为什么这么难?
React
官方也发现了这个问题,在React要重写文档了讲到,React
要基于Hooks
重写文档。
本文主要包括2方面内容:
解释
Hooks
难学的原因给出学习
Hooks
的建议
React的底层架构
可以用一个公式概括React
:
const UI = fn(state);
视图
可以看作状态
经过函数
的映射。
用户与界面的交互,可以看作这个公式的不断执行。
这个公式太精简了,没有解释state
(状态)从哪儿来,我们扩展下:
const state = reconcile(update);
const UI = fn(state);
用户交互产生
update
(更新)update
经过reconcile
步骤计算出当前应用的state
fn
将state
映射为视图变化(UI)
我们给fn
起个名字:commit
:
const state = reconcile(update);
const UI = commit(state);
那么update
在哪里产生呢?当然来自于用户交互,比如:点击事件。
所以React
的底层架构可以简化为三步:
用户交互产生
update
state
= reconcile(update
);UI
= commit(state
);
了解了底层架构,我们再来看通过类比ClassComponent
学习Hooks
会带来的问题。
生命周期函数的抽象层级
我们已经有了完整的驱动视图更新的底层架构,开发者该怎么操作这套架构呢?
可以用计算机的抽象层级来类比:
高层:应用程序
中层:操作系统
底层:计算机组成架构
对应React
:
高层:应用程序 ClassComponent生命周期
中层:操作系统 介入架构的API
底层:计算机组成架构 React底层架构
可以看到,生命周期函数
属于抽象程度比较高的层次。这么设计也是为了让开发者更容易上手React
。
设想一个Vue2
开发者要转React
技术栈,只需要类比Vue
的生命周期来学习React
的生命周期就行了。
这一切在Hooks
到来前都没问题,然而......
Hooks的抽象层级
Hooks
属于中等抽象层级。也就是说,Hooks
直接介入底层架构的运行流程。
高层:应用程序
中层:操作系统 Hooks
底层:计算机组成架构 React底层架构
当我们用生命周期函数
来类比Hooks
时,其实是用高抽象层级
的事物来描述低抽象层级
的事物。
动物 --> 哺乳动物 --> 牛 --> 奶牛
对于一个只见过奶牛,再没见过其他动物的人,你怎么向他解释哺乳动物
是啥?
正是由于抽象层级的不对称,造成通过生命周期函数
类比学习Hooks
会遇到问题。
该怎么学Hooks
既然Hooks
属于中等抽象层,离底层很近,那么更好的学习方式是通过底层向上学习。
祭出我们的三步公式:
用户交互产生
update
state
= reconcile(update
);UI
= commit(state
);
对照公式,我们来讲解几个常见hook
的工作流程:
useState
举个例子:
function App() {const [state, updateState] = useState(0);return <div onClick={() => updateState(state + 1)}></div>;
}
useState
返回值数组包含:
保存的
state
改变
state
的方法updateState
对照公式,state
属于公式步骤2计算得出的:
state = reconcile(update);
此时视图还没有更新。
用户点击div
触发updateState
,对应公式步骤1:
用户交互产生
update
所以调用updateState
能开启底层架构的三步运行流程。
当reconcile
计算出state
后就会进入第三步:
UI
= commit(state
);
最终渲染视图。
useEffect
举个例子:
useEffect(doSomething, [xx, yy])
useEffect
的回调函数doSomething
在第三步执行完成后异步调用:
UI
= commit(state
);
所以在doSomething
函数内部能获取到完成更新的视图。
第二个参数[xx, yy]
作为依赖项,决定了doSomething
是否会被调用。
useLayoutEffect
不同于useEffect
在第三步执行完成后异步调用,useLayoutEffect
会在第三步执行完UI
操作后同步执行。
useRef
以上例子可以看到,useState
与useEffect
分别在三步流程的不同步骤被触发,他们的触发时机是确定的。
那么这三个步骤如何交流呢?通过useRef
。
useState
作用于第一、二步,useLayoutEffect
作用于第三步,useEffect
作用于第三步完成后。
使用useRef
,就能达到在不同步骤间共享引用类型数据的目的。
可以看到,React
为底层架构三步工作流程的每一步提供了对应的hook
,同时提供了串联这三步工作流程的hook
。
开发者只需要根据业务需要,通过基础Hooks
组装出自定义hook
,就能在底层架构运行流程的各个时期运行逻辑。
自底向上学习是本末倒置么?
有同学会反驳:之前学React
得学生命周期函数
的执行时机,现在学Hooks
得学底层架构运行流程
。难道不是本末倒置,更复杂了么?
其实不然。我问你几个问题:
componentWillReceiveProps
为什么被标记为unsafe
?getDerivedStateFromProps
用过么?this.setState
是同步还是异步的?
这些和生命周期函数
相关的问题一点都不简单!很多用了几年React
的前端不一定回答的上。
作为高层次抽象,生命周期函数
隐藏了太多实现细节。同时React
又太灵活,不像Vue
通过模版语言
限制了开发者的操作。
结果就是:不同React
开发者写出各种奇奇怪怪的ClassComponent
。
反观通过底层架构运行流程
学习Hooks
:
底层架构运行流程
就是React
的绝对真理,不会隐藏更多抽象Hooks
的写法规范限制了开发者的奇葩操作
这里唯一的问题,就是缺少一份从底层
出发的文档。这也是官方要重写文档的初衷。
对于熟练使用React
的开发者,在官方新文档出来前,可以参考React技术揭秘[1](点击阅读原文)学习。
这里再提供些其他视角聊Hooks
的文章:
从
理念
层面:代数效应与Hooks[2]从
微观
(代码)层面:所有常见Hooks的源码实现[3]
参考资料
[1]
React技术揭秘: https://react.iamkasong.com/
[2]代数效应与Hooks: https://react.iamkasong.com/process/fiber-mental.html
[3]所有常见Hooks的源码实现: https://react.iamkasong.com/hooks/structure.html
最近组建了一个江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你进群。
················· 若川出品 ·················
今日话题
vue-devtools工具中可以「根据组件直接打开对应文件」,多少人知道或者用过呢。
一个愿景是帮助5年内前端人走向前列的公众号
可加我个人微信 ruochuan12,长期交流学习
推荐阅读
我在阿里招前端,我该怎么帮你?(现在还能加我进模拟面试群)
如何拿下阿里巴巴 P6 的前端 Offer