useEffect这个hooks执行的时机是什么?

1,762 阅读3分钟

useEffect 是 React 中用于处理副作用操作的 Hook。它的执行时机取决于传递给它的第二个参数(依赖数组)以及组件的生命周期。

useEffect 接收两个参数:

  1. 第一个参数是一个函数,这个函数包含了需要执行的副作用操作。
  2. 第二个参数是一个依赖数组,它是一个数组,包含了影响副作用执行的依赖项。

useEffect 有以下三种常见的执行时机:

  1. 无依赖项的情况

    如果你传递了一个空数组作为第二个参数,副作用函数只会在组件挂载时执行一次,类似于 componentDidMount

    useEffect(() => {
      // 这里的代码只会在组件挂载时执行一次
    }, []);
    
  2. 有依赖项的情况

    如果你传递了一个包含依赖项的数组作为第二个参数,副作用函数会在每次这些依赖项发生变化时执行,以及在组件挂载时执行一次。

    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 重新执行。

  3. 没有传递第二个参数的情况

    如果你省略了第二个参数,副作用函数会在每次组件渲染时都执行,包括组件的初次渲染和每次更新。

    useEffect(() => {
      // 这里的代码会在每次组件渲染时执行
    });
    

需要注意的是,在每次执行副作用函数时,React 会在更新之后等待浏览器渲染完成之后再执行它,以确保副作用不会阻塞用户界面的渲染。这意味着副作用函数可能不会立即执行。

总结一下,useEffect 的执行时机取决于组件的生命周期以及传递给它的依赖数组。根据你的需求,可以选择不同的时机来执行副作用操作。useEffect 的依赖数组是基于对象引用比较的,只有当对象引用发生变化时才会重新执行。如果你需要在对象内部的字段值变化时触发 useEffect,确保在更新对象时创建一个新的对象,以便引用发生变化。