Hooks - useState和useEffect

51 阅读3分钟

Hooks

为什么有Hooks?

Hooks之前,类组件和函数组件分工明确:

  • 类组件提供了完整的状态管理和生命周期控制,通常用来承接复杂的业务逻辑
  • 函数组件则是纯粹的从数据到视图的映射,对状态毫无感知。

Hooks是为了解决以下问题:

  • this管理负责问题
  • 生命周期划分不符合"内聚性"原则, 强关联性的逻辑被拆分到不同的生命周期方法中。
  • 组件复用(数据共享或功能复用)的困局。

useState和useEffect

Hooks只能用于React函数式组件,且函数式组件的每一次渲染都是独立的。

useState解析

使用方法:

 const [state, setState] = useState(initialValue);
a.gif

与之前的纯函数组件相比,引入了 useState 这个钩子,打破之前的UI = render(data),函数组件可以从组件之外把状态和修改状态的函数"勾过来", 并且通过调用Setter函数,可以触发组件的重渲染而且每次渲染都有一个独立的状态值

详解

先看组件初次渲染(挂载)时 b.gif

注意:

  • 初次渲染时,通过useState定义多个状态
  • 每次调用useState,都会在组件之外生成一条Hook记录,同时包括状态值(用useState给定的初始值初始化)和修改状态的Setter函数。
  • 多次调用useState生成的Hook记录形成了一条链表。
  • 触发onClick回调函数,调用setS2函数修改s2的状态,不仅修改了Hook记录中的状态值,还可以立即触发重渲染

重渲染:

c.gif

初次渲染结束之后,重渲染之前,Hook记录链表依然存在,逐个调用useState时,useState返回Hook链表中存储的状态,以及修改状态的Setter。

可见,useState,只有在没有的时候才会创建(初次渲染),有时候是直接读取(重渲染时)

总结:

  • 状态和修改状态的Setter函数两两配对,并且后者一定影响前者,前者只被后者影响,作为一个整体完全不受外界影响。
  • 除了useState(和其他钩子),函数组件依然是实现渲染逻辑的纯组件,对状态的管理被Hooks所封装起来。

useEffect解析

使用方法:

    useEffect(effectFn, deps)

effectFn是一个执行某些可能具有副作用的Effect函数(例如数据获取、设置/销毁定时器等),它可以返回一个清理函数(Cleanup)。如定时器

    useEffect(() => {
    });
a.gif

由此可以看出:

  • 每个Effect必须在渲染之后执行,因此不会阻塞渲染,提高了性能。
  • 在运行每个Effect之前,运行前一次渲染的Effect Cleanup函数(如果有的话)
  • 当组件销毁时,运行最后一次Effect的Cleanup函数

再看useEffect的第二个参数:deps(依赖数组),React会在每次渲染后都运行Effect。依赖数组就是用来控制是否应该触发Effect,从而减少不必要的计算。具体而言,只要依赖数组中的每一项与上一次渲染相比没有改变,那么就跳过本次Effect的执行。 最极端情况下,可以指定deps为空数组,可以确保Effect只会在组件初次渲染后执行

a.gif 可以看到后面的渲染都不会触发Effect的执行,只有在组件销毁时,运行Effect Cleanup函数。 ##### 详解 a.gif

注意:

  • useStateuseEffect在每次调用时都被添加到Hook链表中。
  • useEffect还会额外的在一个队列中添加一个等待执行的函数。
  • 在渲染完成后,依次调用Effect队列中的每一个Effect函数。

总结

只在最顶层使用Hook。不要再循环、嵌套、条件语句中使用Hook,这些动态的语句可能导致每次执行组件函数时调用Hook的顺序不能完全一致,导致Hook链表记录的数据失效。

传送门:juejin.cn/post/684490…