Các ứng dụng React được xây dựng từ các component, và mỗi component đều có một vòng đời riêng. Nó xuất hiện trên màn hình. Nó phản hồi với thông tin mới. Cuối cùng, nó biến mất. Nếu bỏ qua nhịp điệu đó, hậu quả sẽ rất rõ rệt. Rò rỉ bộ nhớ sẽ len lỏi vào. Hiệu suất render bị suy giảm. Người dùng nhấn vào các nút không còn phản hồi vì component gắn với chúng đã unmount từ mười phút trước nhưng trình lắng nghe sự kiện vẫn còn sống. Hiểu về vòng đời component không phải là một bài tập học thuật. Đó là nền tảng để viết nên những phần mềm hoạt động ổn định trong thực tế.
Hãy nghĩ về sự tồn tại của một component qua ba giai đoạn. Các nhà phát triển thường mô tả chúng là sinh ra, lớn lên và mất đi. React gọi chúng là mounting, updating và unmounting. Mỗi giai đoạn có một nhiệm vụ cụ thể, và mỗi giai đoạn cho bạn một thời điểm chính xác để chạy code hoặc dọn dẹp nó.
Ba Giai Đoạn
Mounting là sự sinh ra. Component được tạo ra và được chèn vào trình duyệt DOM lần đầu tiên. Đây là cửa sổ thiết lập của bạn. Nếu component cần dữ liệu từ xa, đây là lúc bạn yêu cầu nó. Nếu nó cần một state ban đầu được tính toán từ props, bạn thực hiện việc đó tại đây. Trong kỷ nguyên class component, đây là phạm vi của componentDidMount. Với các functional component hiện đại, bạn xử lý nó bằng useEffect và một mảng dependency rỗng. Chi tiết quan trọng là khối mã này chỉ chạy một lần, ngay sau khi React đặt phần tử vào trang. Điều đó khiến nó trở nên lý tưởng cho việc thiết lập một lần như lấy hồ sơ người dùng hoặc đọc một giá trị từ localStorage.
Updating là sự lớn lên. Một component đã mount không đứng yên một chỗ. Props được gửi đến từ component cha. State thay đổi do tương tác của người dùng. Bản thân component cha cũng re-render, buộc component con phải làm theo. Sau đó, React sẽ re-render component để giữ cho UI đồng bộ với dữ liệu của bạn. Giai đoạn này lặp lại nhiều lần trong suốt vòng đời của một component. Mỗi khi một dependency thay đổi, các effect của bạn sẽ chạy lại, và component sẽ đưa kết quả mới vào DOM. Vì giai đoạn này kích hoạt rất thường xuyên, nên đây cũng là nơi hầu hết các vấn đề về hiệu suất bắt đầu. Những công việc không cần thiết ở đây sẽ tích tụ rất nhanh.
Unmounting là sự mất đi. Component rời khỏi màn hình, có thể vì người dùng đã chuyển sang một trang khác hoặc một bản render có điều kiện đã tắt nó đi. Tại thời điểm này, component bị hủy, nhưng thế giới bên ngoài không phải lúc nào cũng biết điều đó. Các bộ đếm thời gian vẫn tiếp tục chạy. WebSockets vẫn tiếp tục lắng nghe. Các trình lắng nghe sự kiện trên đối tượng window vẫn được gắn chặt. Nếu bạn không dọn dẹp chúng, chúng sẽ giữ các tham chiếu đến các component đã chết và gây rò rỉ bộ nhớ. Trình duyệt lãng phí tài nguyên cho những "bóng ma". React cung cấp cho bạn một hàm cleanup chính xác để bạn có thể dừng các interval, đóng các kết nối và xóa các trình lắng nghe trước khi component biến mất hoàn toàn.
Từ Class Methods đến Hooks
Trong nhiều năm, các kỹ sư React đã quản lý các giai đoạn này thông qua class components. Logic được chia nhỏ qua các phương thức riêng biệt. Bạn lấy dữ liệu bên trong componentDidMount, phản hồi với các thay đổi của prop bên trong componentDidUpdate, và dọn dẹp bên trong componentWillUnmount. Vấn đề là các logic liên quan cuối cùng lại bị phân tán. Một tính năng duy nhất, chẳng hạn như kết nối chat trực tiếp, yêu cầu mã ở ba phương thức lifecycle riêng biệt chỉ để kết nối, kiểm tra thay đổi luồng và ngắt kết nối.
Hooks đã thay đổi điều đó. Hook useEffect trong các functional components cho phép bạn nhóm logic theo mục đích thay vì theo giai đoạn lifecycle. Tuy nhiên, mô hình tư duy vẫn khớp một cách rõ ràng nếu bạn biết tìm ở đâu.
Để xử lý mounting, hãy viết useEffect(() => { ... }, []). Mảng dependency rỗng cho React biết rằng effect này không có các phụ thuộc bên ngoài, vì vậy nó chỉ chạy sau lần render đầu tiên. Điều này thay thế cho componentDidMount.
Để xử lý các cập nhật, hãy bao gồm các giá trị cụ thể mà React nên theo dõi: useEffect(() => { ... }, [dependency]). Khi dependency thay đổi giữa các lần render,
