React Hook là một tính năng hữu ích trong React 16.8 mang lại cho chúng ta một cách đơn giản và hiệu quả trong cách chúng ta quản lý state và side effects trong ứng dụng. Trong bài viết này, mình sẽ chia sẻ các thủ thuật và best practice để giúp các bạn không chỉ hiểu rõ mà còn sử dụng tốt nhất các React hooks.
Tại sao React Hooks lại quan trọng?
Trước khi đi vào các thủ thuật cụ thể, hãy cùng tìm hiểu lý do tại sao React Hooks lại quan trọng và cách chúng cải thiện quá trình phát triển ứng dụng React.
- Đơn giản hóa codebase: Hooks cho phép bạn sử dụng state và các tính năng khác của React mà không cần viết class component.
- Tái sử dụng các logic code: Với Custom Hooks, bạn có thể trừu tượng hóa và tái sử dụng logic giữa các stateful component.
- Cải thiện hiệu suất: Hooks giúp bạn kiểm soát side effects một cách chính xác, giảm thiểu render không cần thiết và tối ưu hóa hiệu suất ứng dụng.
Tuân thủ các quy ước của Hook
Cách đặt tên hợp lý
Luôn luôn bắt đầu tên của Custom Hooks bằng từ use. Điều này không chỉ là một quy ước mà còn giúp React xác định liệu một hàm có phải là một Hook hay không. Ví dụ:
- ✅ Đúng: useFetch, useFormValidation, useAuth.
- ❌ Sai: fetchData, formValidationHook, authHook.
Việc tuân thủ quy ước đặt tên giúp code của bạn dễ đọc hơn và tránh nhầm lẫn.
Sử dụng hooks ở top level
Hooks nên được gọi ở cấp độ trên cùng của functional component hoặc trong các Custom Hooks. Tránh gọi Hooks bên trong bất kỳ điều kiện nào như if, for, hoặc hàm lồng nhau để React có thể duy trì đúng thứ tự của các Hooks. Điều này đảm bảo hành vi nhất quán và ngăn ngừa lỗi không mong muốn.
Ví dụ không tốt:
// Không nên gọi Hook bên trong điều kiện
if (condition) {
const [state, setState] = useState(initialState);
}
Ví dụ tốt:
// Gọi Hook ở cấp độ trên cùng
const [state, setState] = useState(condition ? initialState : anotherState);
Tách biệt logic UI và business logic
Giữ UI logic và business logic tách biệt giúp code dễ bảo trì và hiểu hơn. Sử dụng Custom Hooks để trừu tượng hóa logic phức tạp, giúp component của bạn trở nên sạch sẽ và tập trung vào việc render giao diện.
Tạo một Custom Hook để quản lý business logic:
function useCustomBusinessLogic() {
const [data, setData] = useState([]);
useEffect(() => {
async function fetchData() {
const fetchedData = await fetchSomeData();
setData(fetchedData);
}
fetchData();
}, []);
return data;
}
Sau đó sử dụng Hook trong component UI:
function MyComponent() {
const data = useCustomBusinessLogic();
return (
<div>
{data.map(item => (
<ItemComponent key={item.id} data={item} />
))}
</div>
);
}
Tránh sử dụng quá nhiều Hooks trong một component
Sử dụng quá nhiều Hooks trong một component có thể làm cho component đó trở nên khó quản lý và đọc hiểu. Hãy tạo và sử dụng Custom Hooks để tách biệt các phần logic khác nhau.
Ví dụ không tối ưu:
function MyComponent() {
const theme = useTheme();
const auth = useAuth();
const data = useData();
const settings = useSettings();
// ... nhiều Hooks khác
return (
<div>
{/* Component UI */}
</div>
);
}
Ví dụ tối ưu hơn:
function usePageState() {
const theme = useTheme();
const auth = useAuth();
const data = useData();
const settings = useSettings();
return { theme, auth, data, settings };
}
function MyComponent() {
const { theme, auth, data, settings } = usePageState();
return (
<div>
{/* Component UI */}
</div>
);
}
Kiểm soát side effects tốt
Side effects là các hành động như fetch dữ liệu, cập nhật DOM, hoặc đăng ký sự kiện phải được thực hiện trong useEffect. Quản lý dependency array một cách chính xác để tránh chạy useEffect không cần thiết hoặc bị thiếu dependency.
Ví dụ về quản lý side effect:
useEffect(() => {
const handler = () => {
// Logic xử lý khi window thay đổi kích thước
};
window.addEventListener('resize', handler);
return () => {
window.removeEventListener('resize', handler); // Cleanup để tránh rò rỉ bộ nhớ
};
}, []); // Chỉ chạy một lần khi component mount
Lưu ý về dependency array
- Luôn xác định tất cả các dependency: Để đảm bảo side effects được thực hiện đúng lúc.
- Tránh để trống dependency array một cách không cần thiết: Điều này có thể dẫn đến việc không cập nhật khi state hoặc props thay đổi.
- Sử dụng useCallback và useMemo khi cần thiết: Để tránh tạo ra các hàm hoặc giá trị mới mỗi khi render, gây trigger useEffect không cần thiết.
Tránh Render Không Cần Thiết
Một trong những vấn đề phổ biến khi làm việc với React là render không cần thiết, đặc biệt là khi xử lý các phép tính phức tạp hoặc cập nhật state. Để tránh tình trạng này, chúng ta cần sử dụng các kỹ thuật memoization như useMemo và useCallback.
useMemo: ghi nhớ kết quả của một phép tính để tránh tính lại mỗi khi component re-render.
const expensiveCalculation = useMemo(() => {
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += number;
}
return result;
}, [number]);
useCallback: Memoize các hàm callback để tránh tạo lại hàm mới mỗi khi component re-render.
const handleIncrement = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
Vậy khi nào chúng ta nên sử dụng memoization?
- Khi phép tính phức tạp: Nếu bạn có một phép tính tốn nhiều thời gian, useMemo có thể giúp giảm tải.
- Khi truyền hàm xuống component con: Sử dụng useCallback để tránh render lại component con không cần thiết.
- Không nên lạm dụng: Việc memoization cũng tiêu tốn bộ nhớ, nên chỉ sử dụng khi thực sự cần thiết.
Tận dụng useImperativeHandle
khi cần thiết
useImperativeHandle
trong React cho phép bạn tùy chỉnh giá trị ref của component con, giúp component cha có thể truy cập và điều khiển các phương thức hoặc thuộc tính cụ thể. Điều này hữu ích khi bạn muốn tạo ra một API cụ thể từ component con và để component cha có thể sử dụng các phương thức đó.
Ví dụ:
function ChildComponent(props, ref) {
useImperativeHandle(ref, () => ({
focus: () => {
// Logic để focus vào một element
}
}));
}
export default forwardRef(ChildComponent);
Sau đó sử dụng trong component cha như sau:
const childRef = useRef();
function handleClick() {
childRef.current.focus();
}
<ChildComponent ref={childRef} />
<button onClick={handleClick}>Focus Child</button>
Cẩn thận với các hàm bất đồng bộ trong useEffect
Khi sử dụng các hàm bất đồng bộ trong useEffect
, bạn cần chú ý để tránh các vấn đề không mong muốn.
❌ Ví dụ sai:
useEffect(() => {
async function fetchData() {
const result = await axios.get('/api/data');
setData(result.data);
}
fetchData();
}, []);
✅ Ví dụ đúng:
useEffect(() => {
(async () => {
const result = await axios.get('/api/data');
setData(result.data);
})();
}, []);
Hy vọng rằng những thủ thuật và hướng dẫn này sẽ giúp bạn tự tin hơn khi sử dụng React Hooks trong dự án của mình. Và hãy luôn cập nhật và thực hành để nắm vững các kỹ thuật mới, giúp nâng cao chất lượng và hiệu suất cho ứng dụng của bạn. Cảm ơn các bạn đã theo dõi bài viết của mình.