文章目錄
- 前言
- 一、為什么需要 `useEffect`?
- 核心作用:
- 二、`useEffect` 的基礎用法
- 1. 基本語法
- 2. 依賴項數組的作用
- 三、依賴項數組演示
- 1. 空數組 `[]`:
- 2.無依賴項(空)
- 3.有依賴項
- 四、清理副作用函數
- 實戰案例演示
- 1. 清除定時器
- 2. 取消網絡請求
- 總結
前言
在 React 函數組件中,useEffect
是處理副作用(Side Effects)的核心 Hook。無論是數據獲取、訂閱事件、手動操作 DOM,還是其他異步任務,useEffect
都能幫助開發者優雅地管理這些副作用。然而,它的使用場景復雜且容易踩坑。本文將帶你從基礎到實戰,全面掌握 useEffect
的核心原理和最佳實踐。
一、為什么需要 useEffect
?
在類組件中,副作用通常通過生命周期方法(如 componentDidMount
、componentDidUpdate
、componentWillUnmount
)管理。但在函數組件中,由于沒有生命周期方法,React 提供了 useEffect
來統一處理這些場景。
核心作用:
- 數據獲取:在組件掛載后從 API 獲取數據。
- 訂閱/取消訂閱:如 WebSocket、事件監聽等。
- 手動操作 DOM:如調整滾動位置、聚焦輸入框。
- 清理副作用:避免內存泄漏或重復操作。
二、useEffect
的基礎用法
1. 基本語法
import { useEffect } from 'react';function MyComponent() {useEffect(() => {// 副作用代碼return () => {// 清理函數(可選)};}, [dependencies]); // 依賴項數組(可選) return <div>...</div>;}
2. 依賴項數組的作用
- 空數組:只在組件掛載時執行一次(類似
componentDidMount
)。 - 無依賴項:每次組件渲染后都會執行(類似
componentDidUpdate
,但容易引發性能問題)。 - 有依賴項:當依賴項變化時重新執行。
三、依賴項數組演示
1. 空數組 []
:
只在組件掛載時執行一次(類似 componentDidMount
)**
場景:組件掛載時執行一次初始化操作(如獲取初始數據、訂閱事件)。
import { useState, useEffect } from 'react';function InitialDataLoader() {const [data, setData] = useState(null);useEffect(() => {async function fetchInitialData() {const response = await fetch('/api/initial-data');const result = await response.json();setData(result);}fetchInitialData();}, []); // 空數組:僅在組件掛載時執行if (!data) return <div>Loading initial data...</div>;return <div>{JSON.stringify(data)}</div>;}
useEffect
僅在組件掛載時執行一次,適合初始化操作。- 清理函數(可選)通常用于取消訂閱或清除資源(如
AbortController
)。
2.無依賴項(空)
無依賴項:每次組件渲染后都會執行(類似 componentDidUpdate
,但容易引發性能問題
場景:每次組件渲染后都執行某些操作(如記錄日志、強制更新)。
注意:無依賴項的 useEffect
通常不推薦使用,除非明確需要每次渲染后執行。
import { useEffect } from 'react';function LoggerComponent() {useEffect(() => {console.log('Component rendered or updated');}); // 無依賴項:每次渲染后都會執行return <div>Check the console for render logs.</div>;}
- 每次組件渲染(包括狀態更新、父組件更新等)后都會觸發
useEffect
。 - 慎用:可能導致性能問題(如無限循環、頻繁 API 調用)。
3.有依賴項
:當依賴項變化時重新執行
場景:依賴項(如狀態、props)變化時重新執行副作用(如數據更新、重新訂閱)。
import { useState, useEffect } from 'react';function UserData({ userId }) {const [user, setUser] = useState(null);useEffect(() => {async function fetchUser() {const response = await fetch(`/api/users/${userId}`);const data = await response.json();setUser(data);}fetchUser();}, [userId]); // 依賴項:userId 變化時重新執行 if (!user) return <div>Loading user data...</div>;return <div>{user.name}</div>;}// 父組件使用示例function App() {const [currentUserId, setCurrentUserId] = useState(1);return (<div><button onClick={() => setCurrentUserId(2)}>Load User 2</button><UserData userId={currentUserId} /></div>);}
- 當
userId
變化時(如點擊按鈕切換用戶),useEffect
會重新執行,獲取新用戶數據。 - 依賴項可以是狀態、props 或其他變量,確保副作用與組件狀態同步。
四、清理副作用函數
在 React 的 useEffect 中,清理副作用函數(cleanup function)用于在組件卸載或依賴項變化時清除之前的副作用(如取消訂閱事件、清除定時器、關閉網絡請求等)。以下是幾個常見的清理副作用函數的示例:
import { useState, useEffect } from "react";function Child() {useEffect(() => {console.log("child 初始化");return () => {console.log("child 卸載了");};});return <div>child</div>;
}
function App() {const [count, setCount] = useState(0);const [state, setState] = useState(true);useEffect(() => {return () => {console.log("我執行了!!!");};}, [count]);return (<><div><button onClick={() => setCount(count + 1)}>+1</button><button onClick={() => setState(!state)}>顯示/隱藏</button></div>{state && <Child />}</>);
}export default App;
實戰案例演示
1. 清除定時器
場景:組件中使用了定時器,需要在組件卸載或依賴項變化時清除定時器。
import { useState, useEffect } from 'react';function TimerComponent() {const [count, setCount] = useState(0);useEffect(() => {const timer = setInterval(() => {setCount((prev) => prev + 1);}, 1000);// 清理函數:清除定時器return () => {clearInterval(timer);};}, []); // 空數組:僅在組件掛載時設置定時;return <div>Count: {count}</div>;}
- 組件卸載時,
clearInterval(timer)
會被調用,避免內存泄漏。
2. 取消網絡請求
場景:組件中發起了網絡請求,需要在組件卸載或依賴項變化時取消請求。
import { useState, useEffect } from 'react';function DataFetcher({ userId }) {const [data, setData] = useState(null);useEffect(() => {const abortController = new AbortController();async function fetchData() {try {const response = await fetch(`/api/users/${userId}`, {signal: abortController.signal,});const result = await response.json();setData(result);} catch (error) {if (error.name !== 'AbortError') {console.error('Fetch error:', error);}}}fetchData();// 清理函數:取消請求return () => {abortController.abort();};}, [userId]); // 依賴項:userId 變化時重新執行if (!data) return <div>Loading...</div>;return <div>{JSON.stringify(data)}</div>;}
總結
useEffect 是 React 函數組件中管理副作用的強大工具,但需要開發者深入理解其工作原理和潛在問題。通過合理使用依賴項、清理函數和函數式更新,可以避免常見的陷阱,寫出高效、穩定的代碼。
希望本文能幫助你更好地掌握 useEffect,并在實際項目中靈活運用!如果有任何疑問或補充,歡迎在評論區留言討論。 🚀