useEffect副作用钩子理解

119 阅读4分钟

1. 定义和基本概念

  • useEffect是React Hook中的一个副作用钩子。副作用是指在函数组件执行过程中,除了返回UI(用户界面)之外产生的其他影响,比如数据获取、订阅外部数据源、手动修改DOM等。
  • 它的基本语法是useEffect(() => { /* 副作用逻辑 */ }, [依赖项数组])。其中,第一个参数是一个函数,这个函数包含了副作用的具体操作。第二个参数是一个可选的依赖项数组,用于控制副作用函数的执行时机。

2. 执行时机

  • 组件挂载时
    • 当组件第一次被渲染并添加到DOM中时,useEffect中的副作用函数会被执行。例如,在一个组件中需要从API获取数据来初始化页面内容,就可以在useEffect中发送请求。
    import React, { useState, useEffect } from "react"; 
    function MyComponent() { 
        const [data, setData] = useState([]); 
        useEffect(() => { 
            // 模拟从API获取数据 
            fetch('https://example.com/api/data') 
                .then(response => response.json()) 
                .then(jsonData => setData(jsonData)); 
        }, []); 
        
        return ( <div> {data.map((item, index) => ( <p key={index}>{item}</p> ))} </div> ); 
    }
    
    • 在这个例子中,useEffect中的函数在组件挂载时会执行,从API获取数据并更新组件的状态data,然后组件会根据新的状态重新渲染,展示获取到的数据。
  • 组件更新时(依赖项变化)
    • 如果useEffect有依赖项数组,当依赖项中的任何一个值发生变化时,副作用函数就会重新执行。例如,有一个组件根据用户输入的搜索词来获取数据。
    import React, { useState, useEffect } from "react"; 
    function SearchComponent() { 
        const [searchTerm, setSearchTerm] = useState(''); 
        const [searchResults, setSearchResults] = useState([]); 
        useEffect(() => { 
            // 根据搜索词从API获取数据 
            if (searchTerm) { 
                fetch(`https://example.com/api/search?q=${searchTerm}`) 
                    .then(response => response.json()) 
                    .then(jsonData => setSearchResults(jsonData)); 
            } 
        }, [searchTerm]); 
        
        return (
            <div> 
                <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} /> 
                <div> 
                    {
                        searchResults.map((result, index) => ( <p key={index}>{result}</p> ))
                    } 
                </div> 
            </div> 
        ); 
    } 
    
    • 这里useEffect的依赖项是searchTerm,每当searchTerm的值因为用户输入而改变时,useEffect中的函数就会重新执行,根据新的搜索词获取数据并更新searchResults状态,然后组件重新渲染展示新的搜索结果。
  • 组件卸载时(用于清理操作) return ()=>{}// 卸载时执行
    • useEffect还可以返回一个函数,这个返回的函数会在组件卸载时执行,用于清理副作用。比如,当组件订阅了一个外部数据源或者添加了一个事件监听器,在组件卸载时需要取消订阅或者移除监听器,以避免内存泄漏等问题。
    import React, { useState, useEffect } from "react"; 
    function TimerComponent() { 
        const [count, setCount] = useState(0); 
        useEffect(() => { 
            const interval = setInterval(() => { 
                setCount(count + 1); 
            }, 1000); 
            return () => { clearInterval(interval); }; 
        }, []); 
        
        return ( <div> <p>Seconds passed: {count}</p> </div> ); 
    } 
    
    • 在这个例子中,useEffect中设置了一个每秒更新count状态的定时器。当组件卸载时,返回的函数会执行,清除定时器,防止定时器在组件不存在的情况下继续运行。

3. 依赖项数组的使用和注意事项

  • 空数组([])
    • 当依赖项数组为空时,useEffect中的副作用函数只会在组件挂载时执行一次。这通常用于那些只需要在组件初始化时进行的操作,比如获取初始数据或者设置一些全局状态的初始值。
  • 具体的依赖项(如[variable1, variable2]
    • 当列出具体的依赖项时,要确保依赖项是在组件内部定义的,并且这些依赖项的任何变化都会触发副作用函数的重新执行。如果忘记列出某个依赖项,可能会导致数据不一致或者性能问题。例如,如果一个副作用函数依赖于一个状态变量,但没有将其列入依赖项数组,那么当这个状态变量变化时,副作用函数不会按照预期重新执行,可能会出现错误的结果。
  • 不使用依赖项数组(不推荐)
    • 如果不提供依赖项数组,useEffect中的副作用函数会在每次组件渲染时都执行。这在大多数情况下是不必要的,并且可能会导致性能问题,比如频繁地获取数据或者进行其他昂贵的操作。只有在确实需要副作用函数每次组件渲染都执行的情况下(这种情况比较少见),才可以不使用依赖项数组。