平平无奇古哥哥
2023/05/23阅读:64主题:山吹
对线面试官:为什么不能在if和循环里调用Hooks

面试官:
为什么React的Hooks不能在if里面调用呢?
小明:
因为React的函数式组件每次渲染都会重新生成状态,且每一次渲染都有一个状态序列,如果在if里调用,就可能导致某次渲染的时候状态序列有缺失,从而出现异常。
例如:对于下面这段代码,在React内部的状态序列是0 -> xiaoming。

如果在if里调用,那就会出现只有 0 这样的状态序列,就会出现渲染异常:

面试官:
那你讲讲为什么Hooks要保证状态序列呢?
小明:
因为我们在使用useState声明状态时,只赋给了状态初始值,而并没有给状态加key。
在类组件的时候我们是这样声明状态的:

而在函数式组件中,我们是这样声明状态的:

仔细看就会发现,类组件的状态是以一个对象形式储存的,每个状态都有一个key和value相对应。
而在函数式组件中,useState方法只接受了状态的初始值作为参数,并没有key,所以,函数式组件的状态不能以对象的形式存储,只能以线性表的形式存储,比如说数组和链表。实际上,Hooks状态是用链表来存的。
但是无论是数组还是链表,都需要保持顺序,这样才能使每次渲染的序列对得上。
面试官:
那为什么循环里也不能调用呢?
小明:
因为咱们写的循环里不也有条件语句嘛,比如下面的i < 5就是条件语句:

面试官:
哦哦,那Vue3的setup也是函数式组件,为什么就不存在这种问题呢?
小明:
虽然都是函数式组件,但是还是有区别的。
Vue3的渲染函数只执行一次:在组件初次渲染时,会执行一次 setup()函数并创建响应式对象,然后使用闭包进行缓存。后续的组件渲染会直接使用缓存的渲染函数,而不需要重新执行setup()函数,所以Vue的状态始终就存在一个篮子里,顺序也就不会发生变化。
如下代码所示,setup返回的是一个函数,会形成闭包来对模板和状态就行缓存:

与Vue的setup组件不同的是,React的函数式组件每次更新都需要执行一次渲染函数并返回一个新的视图。原因是React组件返回的是JSX模板,本质上是返回了一个对象,而不是函数,无法形成闭包重复利用。
如代码所示:

而React之所以不采取闭包的方式主要还是为了实现并发模式,因为并发模式要求每次更新的状态都要被保存下来,以便在中断之后可以恢复。
面试官:
好的,回答得还不错,这周末有时间的话过来参加复试吧。
小明:
啊?你们公司周末还上班?
作者介绍