自定義 Hooks(Custom Hooks)
自定義 Hooks(Custom Hooks)
Custom Hooks 是 React 中的一種功能,它允許我們將組件邏輯封裝到一個可重用的函數中。這樣,我們可以把多個組件中共用的邏輯提取出來,而不需要重複編寫代碼。自定義 Hooks 其實就是一個以 “use” 開頭的普通 JavaScript 函數,它可以在內部使用其他 React Hooks,如 useState
、useEffect
等。
自定義 Hooks 的特點
- 邏輯重用:把組件中複雜的邏輯封裝到一個單獨的 Hook 中,減少代碼重複,提升可讀性和可維護性。
- 名稱規範:自定義 Hook 必須以 “use” 開頭,這是 React 用來判斷這個函數是否遵循 Hooks 規則的重要標記。
- 與普通函數不同:自定義 Hooks 是可以使用其他 Hooks 的普通函數,因此可以使用狀態、效應等功能。
1. 自定義 Hook 範例:useFetch
需求:
我們想要在多個組件中實現數據請求功能,但不想在每個組件中都重複編寫相同的邏輯。這時可以使用自定義 Hook 來提取這部分邏輯。
自定義 Hook:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
在這個 useFetch
Hook 中:
- 我們封裝了數據請求的邏輯,通過
fetch
API 進行異步請求。 - 它接收一個
url
作為參數,並返回三個狀態:data
(數據)、loading
(是否加載中)、error
(錯誤信息)。
使用自定義 Hook:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from 'react';
import useFetch from './useFetch';
function Users() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');
if (loading) return <p>加載中...</p>;
if (error) return <p>發生錯誤: {error.message}</p>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default Users;
在這個 Users
組件中,我們使用了 useFetch
來請求用戶數據,並直接利用返回的 data
、loading
和 error
來處理不同的界面顯示。
2. 自定義 Hook 範例:useLocalStorage
需求:
有時候我們想把某些狀態持久化到瀏覽器的 localStorage
中,以便在頁面刷新後仍然保留數據。
自定義 Hook:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.log(error);
}
}, [key, value]);
return [value, setValue];
}
export default useLocalStorage;
在這個 useLocalStorage
Hook 中:
- 我們從
localStorage
中讀取初始值,如果沒有找到則使用默認的initialValue
。 - 每當狀態
value
發生變化時,我們都會把新值存儲到localStorage
中。
使用自定義 Hook:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from 'react';
import useLocalStorage from './useLocalStorage';
function App() {
const [name, setName] = useLocalStorage('name', '');
return (
<div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<p>你好,{name}!</p>
</div>
);
}
export default App;
在這個 App
組件中,我們使用 useLocalStorage
來存儲用戶輸入的名字,並且每次頁面刷新後,輸入框中的內容依然保持。
3. 自定義 Hook 範例:usePrevious
需求:
有時候我們需要獲取上一個渲染周期中的狀態值。這時可以使用自定義的 usePrevious
Hook。
自定義 Hook:
1
2
3
4
5
6
7
8
9
10
11
12
13
import { useRef, useEffect } from 'react';
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
export default usePrevious;
在這個 usePrevious
Hook 中:
- 我們使用
useRef
保存前一個狀態值,並在useEffect
中更新這個值。 - 每次組件重新渲染時,
ref.current
都保存著上一次的值。
使用自定義 Hook:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, { useState } from 'react';
import usePrevious from './usePrevious';
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<div>
<p>當前計數: {count}</p>
<p>上一次計數: {prevCount}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
export default Counter;
在這個 Counter
組件中,我們使用 usePrevious
來獲取上一個計數值,並顯示出來。
4. 自定義 Hooks 的設計原則
- 以 “use” 開頭:這是自定義 Hook 的命名規則,用來告訴 React 它是符合 Hooks 規則的。
- 符合 Hooks 規則:自定義 Hook 需要遵守 React 的 Hooks 規則,不能在循環、條件語句或嵌套函數中調用 Hooks。
- 重用邏輯:當組件中有重複的邏輯時,可以考慮將其提取到自定義 Hook 中,從而提高代碼的可讀性和可維護性。
總結
自定義 Hooks 是 React 中強大的工具,可以讓我們將重複的邏輯封裝並重用,從而使代碼更加模組化、簡潔和可維護。當你發現多個組件中存在相似的邏輯時,考慮將它們提取到自定義 Hook 中,這不僅可以提高開發效率,還能讓代碼更加清晰。
本文章以 CC BY 4.0 授權