Hooks
为什么有Hooks?
Hooks之前,类组件和函数组件分工明确:
- 类组件提供了完整的状态管理和生命周期控制,通常用来承接复杂的业务逻辑
- 函数组件则是纯粹的从数据到视图的映射,对状态毫无感知。
Hooks是为了解决以下问题:
- this管理负责问题
- 生命周期划分不符合"内聚性"原则, 强关联性的逻辑被拆分到不同的生命周期方法中。
- 组件复用(数据共享或功能复用)的困局。
useState和useEffect
Hooks只能用于React函数式组件,且函数式组件的每一次渲染都是独立的。
useState解析
使用方法:
const [state, setState] = useState(initialValue);
与之前的纯函数组件相比,引入了 useState
这个钩子,打破之前的UI = render(data)
,函数组件可以从组件之外把状态和修改状态的函数"勾过来", 并且通过调用Setter函数,可以触发组件的重渲染。
而且每次渲染都有一个独立的状态值
详解
先看组件初次渲染(挂载)时
注意:
- 初次渲染时,通过
useState
定义多个状态 - 每次调用
useState
,都会在组件之外生成一条Hook记录,同时包括状态值(用useState
给定的初始值初始化)和修改状态的Setter函数。 - 多次调用
useState
生成的Hook记录形成了一条链表。 - 触发
onClick
回调函数,调用setS2
函数修改s2
的状态,不仅修改了Hook记录中的状态值,还可以立即触发重渲染。
重渲染:
初次渲染结束之后,重渲染之前,Hook记录链表依然存在,逐个调用useState
时,useState
返回Hook链表中存储的状态,以及修改状态的Setter。
可见,useState,只有在没有的时候才会创建(初次渲染),有时候是直接读取(重渲染时)
总结:
- 状态和修改状态的Setter函数两两配对,并且后者一定影响前者,前者只被后者影响,作为一个整体完全不受外界影响。
- 除了
useState
(和其他钩子),函数组件依然是实现渲染逻辑的纯组件,对状态的管理被Hooks所封装起来。
useEffect解析
使用方法:
useEffect(effectFn, deps)
effectFn
是一个执行某些可能具有副作用的Effect函数(例如数据获取、设置/销毁定时器等),它可以返回一个清理函数(Cleanup)。如定时器
useEffect(() => {
});
由此可以看出:
- 每个Effect必须在渲染之后执行,因此不会阻塞渲染,提高了性能。
- 在运行每个Effect之前,运行前一次渲染的Effect Cleanup函数(如果有的话)
- 当组件销毁时,运行最后一次Effect的Cleanup函数
再看useEffect
的第二个参数:deps
(依赖数组),React会在每次渲染后都运行Effect。依赖数组就是用来控制是否应该触发Effect,从而减少不必要的计算。具体而言,只要依赖数组中的每一项与上一次渲染相比没有改变,那么就跳过本次Effect的执行。
最极端情况下,可以指定deps
为空数组,可以确保Effect只会在组件初次渲染后执行
注意:
useState
和useEffect
在每次调用时都被添加到Hook链表中。useEffect
还会额外的在一个队列中添加一个等待执行的函数。- 在渲染完成后,依次调用Effect队列中的每一个Effect函数。
总结
只在最顶层使用Hook。不要再循环、嵌套、条件语句中使用Hook,这些动态的语句可能导致每次执行组件函数时调用Hook的顺序不能完全一致,导致Hook链表记录的数据失效。