useEffect 是 React 中用于处理副作用操作的 Hook。它的执行时机取决于传递给它的第二个参数(依赖数组)以及组件的生命周期。
useEffect 接收两个参数:
- 第一个参数是一个函数,这个函数包含了需要执行的副作用操作。
- 第二个参数是一个依赖数组,它是一个数组,包含了影响副作用执行的依赖项。
useEffect 有以下三种常见的执行时机:
-
无依赖项的情况:
如果你传递了一个空数组作为第二个参数,副作用函数只会在组件挂载时执行一次,类似于
componentDidMount。useEffect(() => { // 这里的代码只会在组件挂载时执行一次 }, []); -
有依赖项的情况:
如果你传递了一个包含依赖项的数组作为第二个参数,副作用函数会在每次这些依赖项发生变化时执行,以及在组件挂载时执行一次。
const someValue = ...; useEffect(() => { // 这里的代码会在 someValue 发生变化时执行 }, [someValue]);需要注意的是,React 默认情况下无法深度检测对象内部字段值的变化,当你将一个对象作为依赖项传递给
useEffect的依赖数组时,React 会使用严格的引用比较来检测对象是否发生变化。这意味着只有当对象的引用发生变化时,useEffect才会重新执行。我们可以举个例子说明下这点:
import React, { useState, useEffect } from 'react'; function App() { const [person, setPerson] = useState({ name: 'Alice' }); useEffect(() => { console.log('useEffect triggered'); }, [person]); const changeName = () => { // 这里虽然改变了 person 对象的 name 字段的值, // 但是 person 对象的引用仍然没有变化 person.name = 'Bob'; setPerson(person); // 这里没有改变对象的引用 }; return ( <div> <p>{person.name}</p> <button onClick={changeName}>Change Name</button> </div> ); } export default App;在这个示例中,我们有一个
person对象,然后在useEffect中监听了person对象的变化。但是,当我们通过changeName函数改变person对象的name字段时,person对象的引用没有变化,因此useEffect不会重新执行。虽然person对象内部的值发生了变化,但是useEffect并没有捕捉到这个变化。为了解决这个问题,你可以在更新对象时创建一个新的对象,确保对象引用发生变化,示例如下:
const changeName = () => { // 创建一个新的对象,确保对象引用发生变化 const newPerson = { ...person, name: 'Bob' }; setPerson(newPerson); };这样,每次调用
setPerson时都会创建一个新的对象,从而导致对象引用发生变化,触发useEffect重新执行。 -
没有传递第二个参数的情况:
如果你省略了第二个参数,副作用函数会在每次组件渲染时都执行,包括组件的初次渲染和每次更新。
useEffect(() => { // 这里的代码会在每次组件渲染时执行 });
需要注意的是,在每次执行副作用函数时,React 会在更新之后等待浏览器渲染完成之后再执行它,以确保副作用不会阻塞用户界面的渲染。这意味着副作用函数可能不会立即执行。
总结一下,useEffect 的执行时机取决于组件的生命周期以及传递给它的依赖数组。根据你的需求,可以选择不同的时机来执行副作用操作。useEffect 的依赖数组是基于对象引用比较的,只有当对象引用发生变化时才会重新执行。如果你需要在对象内部的字段值变化时触发 useEffect,确保在更新对象时创建一个新的对象,以便引用发生变化。