Anh em đều biết việc sử dụng custom hooks đã trở thành một phần quan trọng của quy trình làm việc. Custom hooks cho phép bạn tách biệt logic, trạng thái và hoạt động phức tạp ra khỏi các components chính, giúp tạo ra mã nguồn sáng sủa hơn, dễ đọc hơn và dễ bảo trì hơn.
Trong bài viết này, chúng ta sẽ khám phá một số custom hooks phổ biến mà bạn có thể tạo và sử dụng trong ứng dụng React của mình. Chúng ta sẽ tìm hiểu cách tạo một custom hook để quản lý dữ liệu trong local storage, làm việc với các request HTTP thông qua một custom hook và cách tạo một custom hook để xử lý form dễ dàng hơn và rất nhiều các hooks khác nữa.
Bằng cách tận dụng sức mạnh của custom hooks, bạn sẽ có khả năng tăng hiệu suất phát triển ứng dụng của mình thông qua việc tạo ra các block code có thể tái sử dụng và có tính chất độc lập, từ đó giúp bạn dễ dàng mở rộng và bảo trì code một cách hiệu quả. Dưới đây là những Custom Hooks mà mình hay dùng nhất trong dự án thực tế:
1.useLocalStorage:
Mô tả: Hook này giúp bạn quản lý dữ liệu trong local storage của trình duyệt.
import { useState } from 'react';
interface LocalStorageHook<T> {
storedValue: T;
setValue: (value: T) => void;
}
function useLocalStorage<T>(key: string, initialValue: T): LocalStorageHook<T> {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value: T) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
};
return { storedValue, setValue };
}
// Sử dụng trong component
function App() {
const [count, setCount] = useLocalStorage('count', 0);
return (
<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment</button></div>
);
}
2.useFetch
Mô tả: Hook này giúp bạn thực hiện các request HTTP và quản lý trạng thái loading và dữ liệu.
import { useState, useEffect } from 'react';
interface FetchResult<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
function useFetch<T>(url: string): FetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const jsonData: T = await response.json();
setData(jsonData);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
// Sử dụng trong component
function App() {
const { data, loading, error } = useFetch('https://api.example.com/data');
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Error: {error.message}</p>;
}
return <div>Data: {JSON.stringify(data)}</div>;
}
3.useForm
Mô tả: Hook này giúp bạn xử lý form dễ dàng hơn bằng cách quản lý giá trị và sự kiện thay đổi của các trường input.
import { useState } from 'react';
interface FormValues {
[key: string]: string;
}
interface FormHook {
values: FormValues;
handleChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
}
function useForm(initialValues: FormValues, onSubmit: (data: FormValues) => void): FormHook {
const [values, setValues] = useState<FormValues>(initialValues);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setValues((prevValues) => ({ ...prevValues, [name]: value }));
};
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
onSubmit(values);
};
return { values, handleChange, handleSubmit };
}
// Sử dụng trong component
function App() {
const handleSubmit = (formData) => {
console.log('Form data:', formData);
};
const { values, handleChange, handleSubmit } = useForm(
{ username: '', password: '' },
handleSubmit
);
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
value={values.username}
onChange={handleChange}
/>
<input
type="password"
name="password"
value={values.password}
onChange={handleChange}
/>
<button type="submit">Submit</button>
</form>
);
}
3.useDebounce
Mô tả: Hook này giúp bạn trì hoãn việc áp dụng thay đổi cho một giá trị trong một khoảng thời gian cố định sau khi ngừng nhập rất hữu ích cho input search.
import { useState, useEffect } from 'react';
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
return debouncedValue;
}
// Sử dụng trong component
function App() {
const [searchTerm, setSearchTerm] = useState<string>('');
const debouncedSearchTerm = useDebounce<string>(searchTerm, 300);
// Sử dụng debouncedSearchTerm để thực hiện tìm kiếm
}
4.useClickOutside
Mô tả: Hook này giúp bạn xác định khi người dùng nhấp chuột ngoài một phần tử cụ thể.
import { useEffect, useRef } from 'react';
function useClickOutside(callback: () => void): React.RefObject<HTMLDivElement> {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback();
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [ref, callback]);
return ref;
}
// Sử dụng trong component
function App() {
const handleOutsideClick = () => {
// Xử lý khi người dùng nhấp chuột ngoài vùng ref
};
const ref = useClickOutside(handleOutsideClick);
return <div ref={ref}>Click outside me</div>;
}
5.useScript
Mô tả: Hook này giúp bạn tải và thực thi một tệp script từ một URL cụ thể.
import { useEffect, useState } from 'react';
interface ScriptStatus {
isLoaded: boolean;
isError: boolean;
}
function useScript(src: string): ScriptStatus {
const [scriptStatus, setScriptStatus] = useState<ScriptStatus>({
isLoaded: false,
isError: false,
});
useEffect(() => {
const script = document.createElement('script');
script.src = src;
script.async = true;
const handleLoad = () => {
setScriptStatus({ isLoaded: true, isError: false });
};
const handleError = () => {
setScriptStatus({ isLoaded: false, isError: true });
};
script.addEventListener('load', handleLoad);
script.addEventListener('error', handleError);
document.body.appendChild(script);
return () => {
script.removeEventListener('load', handleLoad);
script.removeEventListener('error', handleError);
document.body.removeChild(script);
};
}, [src]);
return scriptStatus;
}
// Sử dụng trong component
function App() {
const scriptStatus = useScript('https://example.com/script.js');
if (!scriptStatus.isLoaded) {
return <p>Loading script...</p>;
}
if (scriptStatus.isError) {
return <p>Error loading script.</p>;
}
return <p>Script has been loaded and executed.</p>;
}
6.useIsMobile
Mô tả: Hook này giúp xác định xem thiết bị đang sử dụng có phải là thiết bị di động hay không, phù hợp để dùng làm responsive trong các đoạn code logic.
import { useState, useEffect } from 'react';
function useIsMobile(): boolean {
const [isMobile, setIsMobile] = useState<boolean>(() => {
return window.innerWidth <= 768; // Điều chỉnh breakpoint cho thiết bị di động tùy ý
});
useEffect(() => {
function handleResize() {
setIsMobile(window.innerWidth <= 768); // Điều chỉnh breakpoint cho thiết bị di động tùy ý
}
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return isMobile;
}
// Sử dụng trong component
function App() {
const isMobile = useIsMobile();
return <p>{isMobile ? 'Mobile' : 'Not Mobile'}</p>;
}
7.useElementDimensions
Mô tả: Hook useElementDimensions
được dùng để lấy thông tin về kích thước của một phần tử DOM cụ thể và cập nhật những kích thước đó khi resize hoặc scroll trang web.
import { useEffect, useCallback, useRef, useState } from 'react';
const useElementDimensions = () => {
const ref = useRef<HTMLDivElement | null>(null);
const [dimensions, setDimensions] = useState<DOMRect | null>(null);
const refresh = useCallback(() => {
const domRect = ref.current?.getBoundingClientRect();
if (domRect) {
setDimensions(domRect);
}
}, []);
useEffect(() => {
if (ref.current) {
refresh(); // Gọi ngay lần đầu để cập nhật kích thước ban đầu
const handleResize = () => {
refresh(); // Gọi lại khi cửa sổ thay đổi kích thước
};
const handleScroll = () => {
refresh(); // Gọi lại khi trang web cuộn
};
window.addEventListener('resize', handleResize);
window.addEventListener('scroll', handleScroll, true);
return () => {
window.removeEventListener('resize', handleResize);
window.removeEventListener('scroll', handleScroll, true);
};
}
return () => {};
}, [refresh]);
return { dimensions, ref, refresh };
};
export default useElementDimensions;
Trên đây là tổng hợp các hooks mình hay dùng trong dự án, mình sẽ liên tục cập nhật ở bài viết này. Ngoài ra anh em cũng có thể cài package tên là useHooks để sử dụng mà không cần mất công tìm hoặc tự code một hook của riêng mình. Nhưng anh em nên thử tự tạo 1 hook để biết cách nó hoạt động ra sao, sẽ rất thú vị đấy.
Discussion (undefined)