React精選文章

React Hooks 深度解析與最佳實踐

深入理解React Hooks的工作原理,掌握useState、useEffect、useContext等核心Hook的使用技巧和最佳實踐。

王工程師
18分鐘閱讀
#React#Hooks#前端開發

# React Hooks 深度解析與最佳實踐

React Hooks是React 16.8引入的革命性功能,它讓函數組件也能擁有狀態和生命週期。本文將深入探討Hooks的核心概念和實際應用。

## 1. Hooks的基本概念

Hooks是讓你在函數組件中"鉤入"React狀態和生命週期特性的函數。

### 為什麼需要Hooks?

- **解決類組件的複雜性**:避免this綁定、生命週期方法分散等問題
- **邏輯重用**:自定義Hook讓邏輯更容易在不同組件間共享
- **更好的程式碼組織**:相關邏輯可以放在一起

## 2. useState Hook

useState是最基礎的Hook,用於在函數組件中添加狀態。

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

function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');

return (
<div>
<p>計數: {count}</p>
<button onClick={() => setCount(count + 1)}>
增加
</button>

<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="輸入姓名"
/>
<p>你好, {name}!</p>
</div>
);
}


### useState的最佳實踐

jsx
// 1. 使用函數式更新避免依賴舊狀態
const [count, setCount] = useState(0);

// 好的做法
setCount(prevCount => prevCount + 1);

// 2. 複雜狀態使用useReducer
const [state, dispatch] = useReducer(reducer, initialState);

// 3. 避免在useState中放置計算結果
// 不好的做法
const [fullName, setFullName] = useState(firstName + ' ' + lastName);

// 好的做法
const fullName = firstName + ' ' + lastName;


## 3. useEffect Hook

useEffect用於處理副作用,如API調用、訂閱、手動DOM操作等。

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

function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchUser = async () => {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
} catch (error) {
console.error('獲取用戶資料失敗:', error);
} finally {
setLoading(false);
}
};

fetchUser();
}, [userId]); // 依賴陣列

if (loading) return <div>載入中...</div>;
if (!user) return <div>用戶不存在</div>;

return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}


### useEffect的依賴陣列

jsx
// 1. 空依賴陣列 - 只在組件掛載時執行一次
useEffect(() => {
console.log('組件已掛載');
}, []);

// 2. 有依賴 - 當依賴變化時重新執行
useEffect(() => {
document.title = `新消息: ${messageCount}`;
}, [messageCount]);

// 3. 無依賴陣列 - 每次渲染都執行(通常不推薦)
useEffect(() => {
console.log('每次渲染都執行');
});

// 4. 清理函數
useEffect(() => {
const timer = setInterval(() => {
console.log('定時器執行');
}, 1000);

return () => {
clearInterval(timer); // 清理函數
};
}, []);


## 4. useContext Hook

useContext用於在組件樹中共享資料,避免props drilling。

jsx
import React, { createContext, useContext, useState } from 'react';

// 創建Context
const ThemeContext = createContext();

// Provider組件
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');

const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};

return (

{children}

);
}

// 使用Context的組件
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);

return (
<button
onClick={toggleTheme}
style={{
backgroundColor: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#333' : '#fff',
padding: '10px 20px',
border: '1px solid #ccc'
}}
>
切換主題
</button>
);
}

// 應用根組件
function App() {
return (

<div>
<h1>主題切換範例</h1>

</div>

);
}


## 5. 自定義Hooks

自定義Hook是重用狀態邏輯的強大工具。

jsx
// 自定義Hook: 使用localStorage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});

const setValue = (value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};

return [storedValue, setValue];
}

// 自定義Hook: API調用
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};

fetchData();
}, [url]);

return { data, loading, error };
}

// 使用自定義Hook
function UserList() {
const { data: users, loading, error } = useApi('/api/users');
const [theme, setTheme] = useLocalStorage('theme', 'light');

if (loading) return <div>載入中...</div>;
if (error) return <div>錯誤: {error.message}</div>;

return (
<div>
<h2>用戶列表</h2>
<ul>
{users?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}


## 6. useReducer Hook

useReducer適用於複雜狀態邏輯的場景。

jsx
import React, { useReducer } from 'react';

// 初始狀態
const initialState = {
count: 0,
step: 1
};

// Reducer函數
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + state.step };
case 'decrement':
return { ...state, count: state.count - state.step };
case 'setStep':
return { ...state, step: action.payload };
case 'reset':
return initialState;
default:
throw new Error('未知的action類型');
}
}

function Counter() {
const [state, dispatch] = useReducer(counterReducer, initialState);

return (
<div>
<h2>計數器: {state.count}</h2>
<div>
<button onClick={() => dispatch({ type: 'increment' })}>
增加
</button>
<button onClick={() => dispatch({ type: 'decrement' })}>
減少
</button>
<button onClick={() => dispatch({ type: 'reset' })}>
重置
</button>
</div>
<div>
<label>
步長:
<input
type="number"
value={state.step}
onChange={(e) => dispatch({
type: 'setStep',
payload: parseInt(e.target.value) || 1
})}
/>
</label>
</div>
</div>
);
}


## 7. 性能優化Hooks

### useMemo和useCallback

jsx
import React, { useState, useMemo, useCallback } from 'react';

function ExpensiveComponent({ items, filter }) {
// 使用useMemo緩存計算結果
const filteredItems = useMemo(() => {
console.log('重新計算過濾結果');
return items.filter(item => item.includes(filter));
}, [items, filter]);

// 使用useCallback緩存函數
const handleItemClick = useCallback((item) => {
console.log('點擊項目:', item);
}, []);

return (
<ul>
{filteredItems.map(item => (
<li key={item} onClick={() => handleItemClick(item)}>
{item}
</li>
))}
</ul>
);
}

function ParentComponent() {
const [items, setItems] = useState(['apple', 'banana', 'cherry']);
const [filter, setFilter] = useState('');

return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="過濾項目"
/>

</div>
);
}


## 8. 常見陷阱和最佳實踐

### 1. 避免在useEffect中缺少依賴

jsx
// 錯誤的做法
function Example({ id }) {
const [data, setData] = useState(null);

useEffect(() => {
fetchData(id); // 缺少id依賴
}, []); // 這會導致ESLint警告

// 正確的做法
useEffect(() => {
fetchData(id);
}, [id]);
}


### 2. 避免無限循環

jsx
// 錯誤的做法 - 會導致無限循環
useEffect(() => {
setCount(count + 1);
}, [count]);

// 正確的做法
useEffect(() => {
setCount(prev => prev + 1);
}, []); // 只在掛載時執行一次


### 3. 正確使用清理函數

jsx
useEffect(() => {
const subscription = someAPI.subscribe();

return () => {
subscription.unsubscribe(); // 清理訂閱
};
}, []);


## 總結

React Hooks提供了:

1. **簡化的狀態管理**:useState和useReducer
2. **副作用處理**:useEffect
3. **上下文共享**:useContext
4. **性能優化**:useMemo和useCallback
5. **邏輯重用**:自定義Hooks

掌握這些Hooks的使用技巧,將大大提升React開發效率和程式碼品質。記住Hooks的規則:
- 只在函數組件頂層調用Hooks
- 只在React函數中調用Hooks
- 保持依賴陣列的正確性
React Hooks 深度解析與最佳實踐 | 香港大專CS功課代做