jtwang7 / React-Note

React 学习笔记
8 stars 2 forks source link

React - Hooks (useEffect) #9

Open jtwang7 opened 3 years ago

jtwang7 commented 3 years ago

React - Hooks (useEffect)

参考文章:

effect 副作用

副作用指那些没有发生在数据向视图转换过程中的逻辑。如:

useEffect

在 Hooks 未被提出之前,改变 dom 、发送 ajax 请求以及执行其他包含副作用的操作只能在生命周期函数中使用,函数组件中由于没有生命周期函数,因此不能添加副作用。 useEffect 的出现,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API。

语法

useEffect(func) useEffect 接收一个函数,传给 useEffect 的函数会在浏览器完成布局与绘制之后,在一个延迟事件中被调用。 此外 useEffect 接受的函数还可以返回一个清除函数。清除函数会在组件卸载前执行,如果组件重新渲染,则在执行下一个 effect 之前,上一个 effect 就已被清除。

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // 清除订阅
    subscription.unsubscribe();
  };
});

useEffect 的执行时机

useEffect 在第一次渲染之后和每次更新之后都会执行,与 componentDidMount、componentDidUpdate 不同的是,传给 useEffect 的函数会在浏览器完成布局与绘制之后,在一个延迟事件中被调用。这意味着 useEffect 不会阻塞浏览器对屏幕的更新。

举个例子,假设 useEffect 是同步执行的,它内部包含了一个时间复杂度很高的计算过程,那么当组件 render 结束后,其会先被渲染到屏幕上,然后开始执行 useEffect,由于 useEffect 是同步执行且执行时间较长,此时界面相当于处在了卡死状态,这时候你移动鼠标等操作是无法显示在浏览器上的,因为 useEffect 阻塞了对浏览器的屏幕更新。 因此,useEffect 是异步更新的,每个 useEffect 都会被按顺序推入一个延迟事件队列,并按顺序弹出执行,此时 useEffect 的执行不会阻塞浏览器屏幕的更新,因此你移动鼠标等的操作就会被浏览器实时更新出来。

虽然 useEffect 会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。在开始新的更新前,React 总会先清除上一轮渲染的 effect。

这就保证了每个 useEffect 都会在组件渲染时被执行一次。

并非所有 effect 都可以被延迟执行。例如,一个对用户可见的 DOM 变更就必须在浏览器执行下一次绘制前被同步执行,这样用户才不会感觉到视觉上的不一致。React 为此提供了一个额外的 useLayoutEffect Hook 来处理这类 effect。它和 useEffect 的结构相同,区别只是调用时机不同。

useEffect 的依赖项数组

每次组件渲染都会执行其内部定义的 useEffect,我们可以设置依赖项数组,来保证某个 useEffect 只监测特定值的变化,当某些特定值在两次重渲染之间没有发生变化(Object.is)时,React 会跳过对该 useEffect 的调用。

如果想执行只运行一次的 useEffect(仅在组件挂载和卸载时执行),可以传递一个空数组 [] 作为第二个参数。React 将认定该 useEffect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。

useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);

上述代码只会在 props.source 改变时才会重新调用 useEffect。

useEffect 执行顺序

使用 Hook 其中一个目的就是要解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题。Hook 允许我们按照代码的用途分离他们,React 将按照 effect 声明的顺序依次调用组件中的每一个 effect。

依次将 useEffect 接收的回调函数按顺序推入延迟队列,使用时依次弹出。

jtwang7 commented 3 years ago

React - Hooks (useLayoutEffect)

useLayoutEffectuseEffect 使用类似,其区别在于: useEffect 在全部渲染完毕后(布局+绘制)异步执行;useLayoutEffect 在浏览器布局(layout)之后,绘制(painting)之前同步执行。

useLayoutEffect 使我们可以在 layout 后更正布局,然后再绘制到浏览器上。

使用场景

读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

由于 useLayoutEffect 会在浏览器 layout 之后同步执行,因此它会阻塞浏览器视图的更新。(假设它占用较长时间,页面则出现卡死现象)

除非需要在更新前读取 DOM 布局,否则尽可能使用标准的 useEffect 以避免阻塞视图更新。