前言
還記得在學習 React 的過程中,一開始接觸到 useEffect 時,總是很興奮地想要把它用在各種地方。特別是當遇到任何跟資料處理或狀態更新相關的邏輯時,第一個反應就是:「啊!這裡要用 useEffect!」。但隨著開發經驗的累積,漸漸發現到並非所有情況都適合使用它。今天就讓我分享一下這些實務經驗。
useEffect:React 的「逃生艙」
useEffect 就像是 React 提供給我們的一個逃生艙,讓我們能夠跳出 React 的框架,主要用於處理需要與外部系統同步的情況。但在使用它之前,我們應該先問問自己:這個需求真的需要 useEffect 嗎?
適合使用 useEffect 的情況
與外部系統同步
1
2
3
4
5
6useEffect(() => {
const data = localStorage.getItem('userData');
if (data) {
setUserData(JSON.parse(data));
}
}, []);處理資料擷取的競態問題
1
2
3
4
5
6
7
8
9
10
11
12
13useEffect(() => {
let ignore = false;
async function fetchData() {
const result = await fetchUserData(userId);
if (!ignore) {
setData(result);
}
}
fetchData();
return () => {
ignore = true;
};
}, [userId]);
常見的誤用情況
很多時候,我們其實不需要使用 useEffect。以下是一些常見的誤用案例:
資料轉換
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// ❌ 不建議這樣寫
function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
}
// ✅ 建議這樣寫
function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;
}事件處理後的邏輯
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// ❌ 不建議這樣寫
function ProductPage({ product, addToCart }) {
useEffect(() => {
if (product.isInCart) {
showNotification(`已加入購物車:${product.name}`);
}
}, [product]);
}
// ✅ 建議這樣寫
function ProductPage({ product, addToCart }) {
function handleBuyClick() {
addToCart(product);
showNotification(`已加入購物車:${product.name}`);
}
}
使用 useEffect 時的注意事項
清理函式很重要
1
2
3
4
5
6
7useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(timer);
}, []);正確設定依賴項
- 依賴項陣列要包含 effect 中使用到的所有響應式值
- 可以使用 ESLint 規則來協助檢查
總結
useEffect 是個強大的工具,但不是萬靈丹。在使用它之前,我們應該先思考:
- 這個邏輯是否真的需要「副作用」?
- 是否有其他更適合的解決方案?
- 使用 useEffect 是否會帶來不必要的複雜度?
記住,有時候最簡單的解決方案,反而是最好的選擇。