Using useRef and useEffect to Handle Component Unmount in React

Hòa Nguyễn Coder

Published in Web Developer · Đã đăng vào tháng 2 14, 2025 9:39 AM

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

Nếu bạn thấy đem lại kiến thức bổ ích, hãy Click xem quảng cáo trên trang website của mình nhé! 🚀