官方文档:使用 Effect Hook
Effect Hook 可以让你在函数组件中执行副作用操作。
语法:
useEffect(create, deps);
类型声明:
export function useEffect(create: () => (() => void) | void,deps: Array<mixed> | void | null): void {const dispatcher = resolveDispatcher();return dispatcher.useEffect(create, deps);}
说明:
create
函数返回值:入参函数可以不返回任何值,或返回用于组件卸载时需要清除副作用订阅或监听的逻辑函数(详细查阅 清除函数)useEffect
完成副作用操作。赋值给 useEffect
的函数会在 组件渲染到屏幕之后 执行⚠️ 注意:
effect
将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候 才执行。通常,组件卸载时需要执行清除 effect
创建的诸如订阅或计时器等逻辑。要实现这一点,useEffect
函数需返回一个 清除函数。
以下就是一个创建订阅的例子:
useEffect(() => {const subscription = props.source.subscribe();return () => {// 清除订阅subscription.unsubscribe();};}, []);
⚠️ 注意:
在上述代码示例中,意味着组件的每次更新都会创建新的订阅。若想避免每次更新都触发 effect 的执行,请参阅下一小节 执行时机。
与 componentDidMount
、componentDidUpdate
不同的是,在浏览器完成布局与绘制之后,传给 useEffect
的函数会 延迟调用。这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,因此 不应在函数中执行阻塞浏览器更新屏幕 的操作。
然而,并非所有 effect 都可以被延迟执行。例如,在浏览器执行下一次绘制前,用户可见的 DOM 变更就必须同步执行,这样用户才不会感觉到视觉上的不一致。(概念上类似于被动监听事件和主动监听事件的区别)React 为此提供了一个额外的 useLayoutEffect
Hook 来处理这类 effect。它和 useEffect
的结构相同,区别只是 调用时机 不同。
虽然 useEffect
会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。React 将在组件更新前刷新上一轮渲染的 effect。
默认情况下,effect 会在每轮组件渲染完成后执行。这样的话,一旦 effect 的依赖发生变化,它就会被重新创建。
然而,在某些场景下这么做可能会矫枉过正。比如,在上一章节的订阅示例中,我们不需要在每次组件更新时都创建新的订阅,而是仅需要在 prop.source
改变时重新创建。
要实现这一点,可以给 useEffect
传递第二个参数,它是 effect 所依赖的值数组。更新后的示例如下:
useEffect(() => {const subscription = props.source.subscribe();return () => {subscription.unsubscribe();};}, [props.source]);
此时,只有当 props.source
改变后才会重新创建订阅。
⚠️ 注意:
如果你要使用此优化方式,请确保数组中包含了所有外部作用域中会随时间变化并且在
effect
中使用的变量,否则你的代码会引用到先前渲染中的旧变量。参阅文档,了解更多关于 如何处理函数 以及 数组频繁变化时的措施 内容。如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组(
[]
)作为第二个参数。这就相当于告诉 React 你的 effect 不依赖于props
或state
中的任何值,所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循依赖数组的工作方式。如果你传入了一个空数组(
[]
),effect 内部的props
和state
就会一直拥有其初始值。尽管传入[]
作为第二个参数更接近大家更熟悉的componentDidMount
和componentWillUnmount
思维模式,但我们有更好的方式来避免过于频繁的重复调用 effect。除此之外,请记得 React 会等待浏览器完成画面渲染之后才会延迟调用useEffect
,因此会使得额外操作很方便。我们推荐启用
eslint-plugin-react-hooks
中的exhaustive-deps
规则。此规则会在添加错误依赖时发出警告并给出修复建议。
依赖项数组不会作为参数传给 effect 函数。虽然从概念上来说它表现为:所有 effect 函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能,届时自动创建数组将成为可能。
state
中state
中,因此变化量改变,DOM 渲染