Trong React, chúng ta hay gặp phải một tình trạng là Effect gọi lại nhiều lần. Nay mình sẽ tìm cách xử lý vấn đề này bằng việc sử dụng Unmount trong Effect
Như các bạn cũng biết trong React, có tệp như thế này,
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Chổ <React.StrictMode> cũng làm cho việc Effect gọi lại nhiều lần. Bạn có thể bỏ nó, nhưng bỏ rồi sẽ có một số thông báo lỗi tìm ẩn trong source chúng ta sẽ không nhìn thấy được
Trước tiên mình cứ giữ mặc định , sau đó mình thử sẽ viết code Effect trong App.js hoặc bắt cứ component nào bạn muốn để Fetch API
Ví dụ của mình: Trong ví dụ này mình cài đặt Unmount khi Fetch API "success", bạn nhìn chổ return trong hàm Effect nhé
//check loading fetch api
const [loading, setLoading] = useState(false);
const [categories, setCategories] = useState([]);
const [state, dispatch] = useReducer(reducer, initialTodos);
// ✅ useRef để giữ trạng thái
const componentMount = useRef(false);
//using useEffect call API
useEffect(() => {
componentMount.current = false; // Reset khi effect chạy
const fetchData = async () => {
setLoading(true);
const data = await fetch(URL_API + `/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json", // Đảm bảo request đúng định dạng
Accept: "application/json",
},
body: JSON.stringify({
email: "codetest@example.com",
password: "12345678",
}),
});
let result = await data.json();
// ✅ Kiểm tra trước khi cập nhật state
if (!componentMount.current) {
if (result.status=='success') {
localStorage.setItem('token', result.authorisation.token);
await dispatch({ type: "INSTALL_TOKEN", payload: result.authorisation.token });
// Khi Login thành công, đã có token, ta sẽ gọi lấy tất cả categories từ api/categoires
fetchCategories();
}
setLoading(false);
}
};
// call the function
fetchData().catch(console.error);
return () => {
// ✅ Cleanup khi unmount
componentMount.current = true;
};
}, []);
Đoạn code trên mình giải thích như sau:
loading: Trạng thái để kiểm tra xem API có đang được gọi không. Mặc định là false.
categories: Lưu danh sách danh mục, mặc định là mảng rỗng.
state, dispatch: Sử dụng useReducer để quản lý state phức tạp hơn.
useRef để kiểm soát việc unmount
const componentMount = useRef(false);
useRef giữ giá trị giữa các lần render mà không làm component re-render.
componentMount.current sẽ giúp kiểm soát việc component đã bị unmount hay chưa.
useEffect(() => {
....
return () => {
// ✅ Cleanup khi unmount
componentMount.current = true;
};
}, []);
Còn chổ Fetch API chúng ta cứ việc xử lý gửi data cho server thôi
const fetchData = async () => {
setLoading(true);
const data = await fetch(URL_API + `/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
email: "codetest@example.com",
password: "12345678",
}),
});
let result = await data.json();
setLoading(true);: Bắt đầu hiển thị trạng thái loading.
fetch(URL_API + "/auth/login"): Gửi request POST đến API login.
headers: Đảm bảo request đúng định dạng JSON.
body: Gửi thông tin đăng nhập (email và password).
await data.json();: Chuyển response thành JSON.
Okay quan trọng việc chúng ta kiểm soát được unmount trong Effect, để tranh Component re-render nhiều lần, cũng không tốt