ReactJS - 使用 useEffect:初学者的全面指南

你好,未来的 React 巫师們!今天,我們將踏上一段令人興奮的旅程,探索 React 中的 useEffect。如果你是編程新手,別擔心——我會成為你的友好導遊,我們會一步一步地學習。在本教程結束時,你將能像專業人士一樣熟練使用 useEffect

ReactJS - Using useEffect

useEffect 是什麼?

在我們深入之前,讓我們先了解 useEffect 的相關知識。想像你正在烤蛋糕。你混合食材,放入烤箱,然後呢?你可能會想設定一個計時器,對吧?這就是 useEffect 在 React 中的作用。它讓你在组件中執行“副作用”。

副作用是组件除了渲染 UI 的主要任務外還會發生的行為。這些可能包括:

  • 從 API 獲取數據
  • 手動更改 DOM
  • 設置訂閱

現在,讓我們來深入了解!

useEffect 的簽名

useEffect 钩子有一種特定的寫法。讓我們分解一下:

useEffect(() => {
// 你的副作用代碼在這裡
}, [dependencies]);

這裡的每部分意義如下:

  1. useEffect:我們正在使用的钩子名稱。
  2. () => { ... }:這是一個箭頭函數,我們在裡面放置副作用代碼。
  3. [dependencies]:這是一個可選數組,我們在裡面列出副作用依賴的任何值。

讓我們看一個簡單的例子:

import React, { useEffect, useState } from 'react';

function Timer() {
const [seconds, setSeconds] = useState(0);

useEffect(() => {
const interval = setInterval(() => {
setSeconds(prevSeconds => prevSeconds + 1);
}, 1000);

return () => clearInterval(interval);
}, []);

return <div>秒數:{seconds}</div>;
}

在這個例子中,我們創建了一個簡單的計時器。useEffect 钩子設置了一個間隔,每秒增加我們的 seconds 狀態。空的依賴數組 [] 意味著這個副作用只會在组件挂载時運行一次。

Effect 钩子的特性

現在,我們已經看到了一個基本示例,讓我們探索一下副作用钩子的關鍵特性:

  1. 時間:默認情況下,副作用在每次渲染後運行。
  2. 條件執行:我們可以使用依賴數組控制副作用運行的時間。
  3. 清理:副作用可以返回一個清理函數以防止記憶體泄漏。

讓我們詳細看看這些特性。

時間

默認情況下,useEffect 在每次渲染後運行。這意味著如果你在副作用中更新狀態,它可能會導致無限循環!以下是一個不該這麼做的例子:

function BadExample() {
const [count, setCount] = useState(0);

useEffect(() => {
setCount(count + 1); // 這會導致無限循環!
});

return <div>{count}</div>;
}

條件執行

為了防止副作用不必要地運行,我們可以提供一個依賴數組:

function ConditionalEffect({ userId }) {
const [user, setUser] = useState(null);

useEffect(() => {
fetchUser(userId).then(data => setUser(data));
}, [userId]);

return <div>{user ? user.name : '加載中...'}</div>;
}

在這個例子中,副作用只會在 userId 改變時運行。

清理

有些副作用需要清理以防止記憶體泄漏。以下是如何進行的:

function CleanupExample() {
useEffect(() => {
const subscription = subscribeToSomething();

return () => {
subscription.unsubscribe();
};
}, []);

return <div>我訂閱了!</div>;
}

從副作用返回的函數將在组件卸载時被調用。

使用 Effect 獲取數據

useEffect 的一个常见用途是從 API 獲取數據。讓我們看一個例子:

function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(error => {
setError(error);
setLoading(false);
});
}, []);

if (loading) return <div>加載中...</div>;
if (error) return <div>錯誤:{error.message}</div>;
return <div>數據:{JSON.stringify(data)}</div>;
}

這個组件在挂载时获取数据,处理加载和错误状态,并在数据准备就绪时显示数据。

DOM 更改

useEffect 也可以用来直接操作 DOM。以下是一个例子:

function DOMManipulator() {
useEffect(() => {
const element = document.getElementById('my-element');
element.style.color = 'red';

return () => {
element.style.color = '';
};
}, []);

return <div id="my-element">我是紅色的!</div>;
}

这个副作用在组件挂载时将元素颜色改为红色,并在组件卸载时重置颜色。

清理函數

我們已經接触到了清理函数,但讓我們更深入地了解。清理函数对于防止記憶體泄漏和避免不必要的行为至关重要。以下是一个更复雜的例子:

function WindowResizer() {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);

useEffect(() => {
const handleResize = () => setWindowWidth(window.innerWidth);
window.addEventListener('resize', handleResize);

return () => {
window.removeEventListener('resize', handleResize);
};
}, []);

return <div>窗口宽度:{windowWidth}px</div>;
}

在这个例子中,我们在组件挂载时添加了一个事件监听器,并在组件卸载时移除它。这防止了监听器在我们不再需要它时继续存在。

總結

讓我們總結一下我們學到的關於 useEffect 的知識:

特性 描述
簽名 useEffect(() => { ... }, [dependencies])
時間 默認情况下在每次渲染后运行
條件執行 使用依賴數組控制副作用運行的時間
清理 從副作用返回的函數用於清理
數據获取 可以用来从 API 获取数据
DOM 更改 可以直接操作 DOM
清理函數 对于防止記憶體泄漏至关重要

就这样!你已经迈出了探索 useEffect 的第一步。记住,就像任何强大的工具一样,它需要练习才能掌握。所以不要害怕实验和犯错误——这是我们所有人学习的途径。快乐编码,愿你的副作用总是干净,组件总是响应式!

Credits: Image by storyset