Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Component React chủ yếu mô tả giao diện, nhưng ứng dụng thật còn cần làm những việc bên ngoài thế giới của React: gọi API, đặt timer, đăng ký sự kiện, cập nhật tiêu đề trang. Những việc đó gọi là side effect, và useEffect là nơi bạn đặt chúng.
import { useEffect, useState } from 'react';
function TieuDeTrang() {
const [dem, setDem] = useState(0);
useEffect(() => {
document.title = 'Bạn đã bấm ' + dem + ' lần';
}, [dem]);
return <button onClick={() => setDem(dem + 1)}>Bấm ({dem})</button>;
}Hàm truyền vào chạy sau khi React cập nhật giao diện. Mảng thứ hai gọi là dependency array, quyết định khi nào effect chạy lại.
[]: chạy một lần sau lần render đầu tiên, thích hợp cho khởi tạo.[a, b]: chạy sau lần đầu và mỗi khi a hoặc b thay đổi.Quy tắc vàng: mọi giá trị từ component (props, state, biến, hàm) mà effect sử dụng phải có trong mảng. Công cụ ESLint của React (plugin react-hooks) sẽ cảnh báo nếu bạn thiếu, hãy nghe theo nó.
Effect có thể trả về một hàm dọn dẹp. React gọi nó trước khi chạy lại effect và khi component bị gỡ khỏi màn hình:
function DongHo() {
const [gio, setGio] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setGio(new Date()), 1000);
return () => clearInterval(id); // dọn dẹp
}, []);
return <p>{gio.toLocaleTimeString('vi-VN')}</p>;
}Nếu quên cleanup, timer vẫn chạy sau khi component biến mất, gây rò rỉ bộ nhớ và lỗi cập nhật state trên component đã gỡ. Tương tự với addEventListener cần removeEventListener.
function DanhSachBai({ chuDe }) {
const [bai, setBai] = useState([]);
const [loi, setLoi] = useState(null);
const [dangTai, setDangTai] = useState(true);
useEffect(() => {
const controller = new AbortController();
setDangTai(true);
fetch('/api/bai-viet?chuDe=' + encodeURIComponent(chuDe), {
signal: controller.signal,
})
.then(r => {
if (!r.ok) throw new Error('Lỗi ' + r.status);
return r.json();
})
.then(setBai)
.catch(e => { if (e.name !== 'AbortError') setLoi(e.message); })
.finally(() => setDangTai(false));
return () => controller.abort();
}, [chuDe]);
if (dangTai) return <p>Đang tải...</p>;
if (loi) return <p>{loi}</p>;
return <ul>{bai.map(b => <li key={b.id}>{b.tieuDe}</li>)}</ul>;
}Khi chuDe đổi, cleanup hủy yêu cầu cũ rồi gọi yêu cầu mới, tránh tình trạng kết quả cũ về muộn ghi đè kết quả mới. Trong dự án lớn, nhiều đội dùng thư viện lấy dữ liệu như TanStack Query hay SWR để xử lý chuyện này gọn hơn.
useMemo, useCallback khi thực sự cần.Một ví dụ khác về đăng ký và dọn dẹp sự kiện là theo dõi chiều rộng cửa sổ:
function useChieuRong() {
const [rong, setRong] = useState(window.innerWidth);
useEffect(() => {
const xuLy = () => setRong(window.innerWidth);
window.addEventListener('resize', xuLy);
return () => window.removeEventListener('resize', xuLy);
}, []);
return rong;
}Hàm cleanup gỡ đúng trình lắng nghe đã đăng ký, vì dùng cùng tham chiếu hàm xuLy. Đây cũng là một custom hook đơn giản: bạn có thể dùng nó ở bất kỳ component nào bằng const rong = useChieuRong() mà không lặp lại logic.
useEffect là cầu nối giữa component và thế giới bên ngoài. Hãy nhớ khai báo đủ dependency, luôn dọn dẹp những gì mình đăng ký và chỉ dùng effect khi thật sự cần đồng bộ với bên ngoài.
Để thực hành useEffect qua dự án có gọi API thật, bạn có thể tham khảo khóa học tại codebem.com.
Nguyễn Đình Cường
0 comment