📋 Mục lục
useState– Cơ sở của trạng tháiuseEffect– Hiệu ứng phụ và vòng đờiuseContext– Chia sẻ trạng thái mà không cần propsuseReducer– Logic trạng thái phức tạp và có thể dự đoánuseRef– Tham chiếu, lưu trữ và thoátuseMemovàuseCallback– Tối ưu hóa hiệu suất- Custom Hooks – Tạo logic tái sử dụng
- Lỗi thường gặp và giải pháp
Custom Hooks là gì và tại sao chúng lại mạnh mẽ?
Custom Hooks (Hooks tùy chỉnh) là một trong những tính năng mạnh mẽ nhất của Hooks trong React. Chúng cho phép bạn trích xuất và tái sử dụng logic có trạng thái từ một thành phần. Custom Hooks không phải là một hook mới trong React, mà là một quy ước: một hàm JavaScript tuân theo hai quy tắc:
- Tên của nó phải bắt đầu bằng
use(ví dụ:useFetch,useLocalStorage). - Có thể gọi các hook khác (như
useState,useEffect, v.v.).
Hãy nghĩ về chúng như những mảnh ghép Lego trong lập trình. Bạn cần biết xem người dùng có đang online hay không? useOnlineStatus. Bạn muốn tương tác với Local Storage? useLocalStorage. Bạn cần lấy dữ liệu từ nhiều nguồn? useFetch.
Tạo Custom Hook đầu tiên: useFetch
Việc lấy dữ liệu là một trường hợp sử dụng hoàn hảo cho Custom Hook. Bạn gần như luôn cần quản lý trạng thái tải, xử lý lỗi và dữ liệu thu được. Hãy bao bọc toàn bộ logic đó trong một hook có thể tái sử dụng.
Mã của hook useFetch.js:
javascript
// hooks/useFetch.js
import { useState, useEffect } from 'react';
export function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Nếu URL không hợp lệ, không làm gì cả.
if (!url) return;
const controller = new AbortController();
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) {
throw new Error(`Lỗi: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
if (!controller.signal.aborted) {
setLoading(false);
}
}
};
fetchData();
return () => {
controller.abort();
};
}, [url]);
return { data, loading, error };
}
Sử dụng useFetch trong một thành phần
Bây giờ, việc tiêu thụ logic phức tạp này trở nên cực kỳ đơn giản.
javascript
// components/GitHubUser.js
import React, { useState } from 'react';
import { useFetch } from '../hooks/useFetch';
function GitHubUser() {
const [username, setUsername] = useState('facebook');
const [input, setInput] = useState('facebook');
const { data, loading, error } = useFetch(`https://api.github.com/users/${username}`);
const handleSubmit = (e) => {
e.preventDefault();
setUsername(input);
}
return (
<div>
<h3>Buscador de Usuarios de GitHub</h3>
<form onSubmit={handleSubmit}>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Introduce un usuario de GitHub"
/>
<button type="submit">Buscar</button>
</form>
{loading && <p>Cargando...</p>}
{error && <p>Error: {error}</p>}
{data && (
<div>
<h4>{data.name} (@{data.login})</h4>
<img src={data.avatar_url} alt={data.name} width="100" />
<p>{data.bio}</p>
</div>
)}
</div>
);
}
Như bạn có thể thấy, thành phần GitHubUser không cần biết gì về useState hay useEffect. Nó chỉ quan tâm đến kết quả cuối cùng: data, loading và error. Điều này làm cho mã trở nên cực kỳ sạch sẽ, dễ hiểu và dễ bảo trì.
✅ Thực tiễn tốt nhất và mẫu thường gặp
- Tên bắt đầu bằng
use: Đây là quy ước bắt buộc. Điều này giúp React và các công cụ linting biết rằng hàm của bạn là một hook và cần tuân thủ các quy tắc của hooks. - Trả về một mảng hoặc một đối tượng: Nếu hook của bạn trả về nhiều giá trị, hãy trả về dưới dạng một đối tượng
{ value1, value2 }để việc tiêu thụ trở nên dễ đọc hơn. Nếu là một hook rất tổng quát, có thể trả về một mảng[value, updater]. - Thiết kế tổng quát và có thể cấu hình: Một Custom Hook tốt chấp nhận tham số để điều chỉnh hành vi của nó. Trong
useFetch, có thể chấp nhận một đối tượng tùy chọn chofetch. - Không phá vỡ quy tắc của hooks: Trong Custom Hook, hãy tiếp tục tuân thủ cùng các quy tắc: không gọi chúng trong vòng lặp, điều kiện hoặc hàm lồng nhau.
🚨 Lỗi thường gặp và cách tránh
- Lỗi: Quên tiền tố
use.- Giải pháp: Luôn đặt tên cho Custom Hook của bạn theo định dạng
useMiHook. Nếu không, React sẽ không thể xác minh bạn đang tuân thủ quy tắc của hooks.
- Giải pháp: Luôn đặt tên cho Custom Hook của bạn theo định dạng
- Lỗi: Chia sẻ trạng thái giữa các thành phần.
- Giải thích: Là một lỗi về khái niệm khi nghĩ rằng một Custom Hook chia sẻ cùng một trạng thái giữa các thành phần khác nhau sử dụng nó. Mỗi lần gọi Custom Hook hoàn toàn độc lập và có trạng thái nội bộ riêng. Nếu bạn muốn chia sẻ trạng thái, bạn cần
useContexthoặc một thư viện quản lý trạng thái toàn cục.
- Giải thích: Là một lỗi về khái niệm khi nghĩ rằng một Custom Hook chia sẻ cùng một trạng thái giữa các thành phần khác nhau sử dụng nó. Mỗi lần gọi Custom Hook hoàn toàn độc lập và có trạng thái nội bộ riêng. Nếu bạn muốn chia sẻ trạng thái, bạn cần
🚀 Thử thách thực tiễn
-
useLocalStorage: Tạo một hook đồng bộ vớilocalStorage. Nó nên hoạt động tương tự nhưuseState, nhưng lưu trữ giá trị trong trình duyệt.javascript// Sử dụng mong đợi: const [name, setName] = useLocalStorage('username', 'Invitado'); -
useOnlineStatus: Tạo một hook trả vềtruenếu trình duyệt đang kết nối internet vàfalsenếu không. (Gợi ý:window.addEventListener('online', ...)vàwindow.addEventListener('offline', ...)). -
useDebounce: Một hook rất hữu ích. Nó nhận một giá trị (ví dụ: văn bản từ input) và trả về một phiên bản "trì hoãn" của giá trị đó chỉ được cập nhật sau một khoảng thời gian không có thay đổi. Rất thích hợp để tránh thực hiện yêu cầu tới API trong mỗi lần nhấn phím.javascript// Sử dụng mong đợi: const [searchTerm, setSearchTerm] = useState(''); const debouncedSearchTerm = useDebounce(searchTerm, 500); // 500ms trì hoãn // Sử dụng `debouncedSearchTerm` trong `useFetch`
Kết luận
Custom Hooks trong React là một công cụ mạnh mẽ giúp các lập trình viên tái sử dụng logic một cách hiệu quả. Bằng cách áp dụng những thực tiễn tốt nhất và tránh những lỗi phổ biến, bạn có thể xây dựng các thành phần React dễ bảo trì và dễ sử dụng hơn. Hãy bắt đầu tạo ra các Custom Hooks của riêng bạn ngay hôm nay để nâng cao hiệu suất và khả năng mở rộng của ứng dụng của bạn!
Câu hỏi thường gặp (FAQ)
-
Custom Hooks có thể sử dụng trong các thành phần class không?
- Không, Custom Hooks chỉ có thể được sử dụng trong các thành phần hàm.
-
Có giới hạn nào đối với số lượng Custom Hooks có thể tạo không?
- Không có giới hạn, bạn có thể tạo bao nhiêu tùy thích miễn là bạn tuân thủ các quy tắc của hooks.
-
Tôi có thể sử dụng hooks bên trong hooks khác không?
- Có, bạn có thể gọi hooks bên trong Custom Hooks miễn là tuân thủ các quy tắc của hooks.
-
Có cần thiết phải kiểm tra lỗi trong Custom Hooks không?
- Có, việc xử lý lỗi giúp cải thiện trải nghiệm người dùng và giúp phát hiện vấn đề sớm hơn.