useEffect là hook mà người mới thường vừa cần sớm, vừa dễ hiểu sai. Bạn sẽ gặp nó khi muốn gọi API, đọc dữ liệu ban đầu, lắng nghe sự kiện, hoặc đồng bộ component với một hệ thống bên ngoài React.
Điều quan trọng: useEffect không phải nơi để viết mọi logic. Nó dành cho những “tác vụ phụ” xảy ra sau khi component render.
useEffect dùng để làm gì?
Trong React Native, UI được render từ state và props. Nhưng app thực tế còn phải làm nhiều việc bên ngoài UI: gọi API, đăng ký listener, đọc AsyncStorage, theo dõi trạng thái mạng, hoặc cleanup timer. Những việc này gọi chung là effect.
import { useEffect, useState } from 'react';
import { ActivityIndicator, Text, View } from 'react-native';
function ArticleScreen() {
const [loading, setLoading] = useState(true);
const [title, setTitle] = useState('');
useEffect(() => {
async function loadArticle() {
setLoading(true);
const response = await fetch('https://example.com/api/article');
const data = await response.json();
setTitle(data.title);
setLoading(false);
}
loadArticle();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return <Text>{title}</Text>;
}
Dấu [] ở cuối nghĩa là effect này chạy sau lần render đầu tiên, thường dùng cho việc load dữ liệu ban đầu.
Dependency array là gì?
Tham số thứ hai của useEffect gọi là dependency array. Nó nói cho React biết effect phụ thuộc vào dữ liệu nào.
useEffect(() => {
// Chạy lại mỗi khi articleId thay đổi
loadArticle(articleId);
}, [articleId]);
Nếu bỏ dependency array, effect sẽ chạy sau mọi lần render. Điều này đôi khi đúng, nhưng với người mới thường gây vòng lặp gọi API liên tục.
Cleanup là gì?
Một số effect cần dọn dẹp khi component bị unmount hoặc trước khi effect chạy lại. Ví dụ timer, listener, subscription. Khi đó bạn return một function cleanup.
useEffect(() => {
const timerId = setInterval(() => {
console.log('Đang chạy timer');
}, 1000);
return () => {
clearInterval(timerId);
};
}, []);
Nếu không cleanup, app có thể bị rò rỉ listener, timer chạy thừa hoặc cập nhật state khi màn hình đã rời đi.
Đừng lạm dụng useEffect
Một lỗi phổ biến là thấy gì cũng đưa vào useEffect. Nếu bạn chỉ cần tính dữ liệu để render UI, hãy tính trực tiếp trong component. Ví dụ nối họ tên, lọc danh sách nhỏ, format label đơn giản thường không cần effect.
// Không cần useEffect
const fullName = `${firstName} ${lastName}`;
Hãy dùng effect khi component cần tương tác với thứ nằm ngoài quá trình render: API, storage, timer, event listener, native module, analytics.
Mẫu gọi API dễ đọc hơn
Khi gọi API thật, bạn nên có đủ trạng thái loading, error và data. Như vậy UI không bị trắng khi lỗi mạng.
const [articles, setArticles] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function loadArticles() {
try {
setLoading(true);
setError(null);
const response = await fetch('https://example.com/api/articles');
const data = await response.json();
if (!cancelled) {
setArticles(data);
}
} catch (err) {
if (!cancelled) {
setError('Không tải được dữ liệu');
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
loadArticles();
return () => {
cancelled = true;
};
}, []);
Biến cancelled giúp tránh cập nhật state sau khi component không còn cần dữ liệu nữa. Với dự án lớn, bạn có thể dùng thư viện chuyên biệt cho data fetching, nhưng hiểu mẫu cơ bản này vẫn rất quan trọng.
Lỗi thường gặp
Lỗi đầu tiên là quên dependency array khiến effect chạy liên tục. Lỗi thứ hai là thêm sai dependency làm dữ liệu không cập nhật khi props thay đổi. Lỗi thứ ba là gọi API nhưng không xử lý loading/error. Lỗi thứ tư là không cleanup listener hoặc timer.
Kết luận
useEffect giúp React Native kết nối UI với thế giới bên ngoài: API, storage, timer, listener và native APIs. Người mới nên nhớ: render UI bằng state/props, còn effect dùng cho tác vụ phụ sau render. Hiểu ranh giới này sẽ giúp code dễ đoán và ít lỗi hơn.
Nguồn tham khảo: React: useEffect, Synchronizing with Effects.





