Tại sao Console của bạn lại hiện đầy cảnh báo
Nâng cấp một ứng dụng React từ phiên bản 15 hoặc 16 lên 17 hoặc 18 thường kích hoạt hàng loạt cảnh báo trên console. Những thông báo này cho biết React đang dần loại bỏ các phương thức lifecycle cũ. Cụ thể là componentWillMount, componentWillReceiveProps, và componentWillUpdate đang nằm trong danh sách sắp bị khai tử.
React đang chuyển hướng sang mô hình concurrent rendering, nơi các phương thức 'will' không còn đáng tin cậy. Chúng có thể bị kích hoạt nhiều lần trước khi một component thực sự render. Hành vi này dẫn đến các side effects không lường trước được hoặc gây rò rỉ bộ nhớ (memory leaks). Để giữ cho ứng dụng ổn định, React hiện yêu cầu thêm tiền tố UNSAFE_ hoặc phải tiến hành refactor hoàn toàn.
Tìm ra thủ phạm
Nhiệm vụ đầu tiên của bạn là xác định nguồn gốc của cảnh báo. Nếu đó là code của bạn, console thường sẽ chỉ ra một dòng cụ thể trong component. Tuy nhiên, nếu cảnh báo đến từ thư mục node_modules, stack trace có thể trông giống như một mớ hỗn độn của code đã bị minified.
Đừng đoán mò. Hãy sử dụng tính năng tìm kiếm toàn cục trong VS Code (Cmd+Shift+F) cho chuỗi componentWillMount(). Nếu kết quả trả về nằm trong thư mục src của bạn, bạn có việc phải làm rồi đấy. Nếu kết quả chỉ nằm trong các thư viện bên thứ ba như react-router v3 hoặc material-ui v0.x, bạn nên ưu tiên cập nhật các dependency đó lên phiên bản hiện đại.
Giải pháp 1: Sửa nhanh trong 30 giây (Tiền tố UNSAFE_)
Sử dụng phương pháp này nếu bạn đang quản lý một codebase khổng lồ và cần tắt cảnh báo ngay lập tức. Đây chỉ là một giải pháp tình thế. Nó đáp ứng quy ước đặt tên mới của React mà không làm thay đổi cách thức hoạt động thực tế của component.
// Trước: Cách đã bị loại bỏ
class LegacyComponent extends React.Component {
componentWillMount() {
console.log('Component is about to mount');
}
}
// Sau: Cách dùng tiền tố để tạm thời sử dụng
class LegacyComponent extends React.Component {
UNSAFE_componentWillMount() {
console.log('Component is about to mount');
}
}
Hãy nhớ rằng: cách này chỉ làm tắt cảnh báo. Nó không giải quyết được các vấn đề cốt lõi về kiến trúc vốn đã khiến phương thức này trở nên nguy hiểm ngay từ đầu.
Giải pháp 2: Cập nhật hàng loạt với react-codemod
Việc đổi tên thủ công hàng chục lifecycle rất tẻ nhạt. Đội ngũ React đã xây dựng công cụ codemod để xử lý công việc nặng nhọc này thay bạn. Bạn có thể cập nhật toàn bộ thư mục src trong vài giây bằng cách chạy lệnh sau:
npx react-codemod rename-unsafe-lifecycles
Script này sẽ quét các file của bạn và tự động thêm UNSAFE_ vào trước tất cả các phương thức đã bị loại bỏ. Luôn commit tiến trình hiện tại của bạn trước khi chạy lệnh này. Điều đó giúp bạn dễ dàng xem lại sự thay đổi (diff) trước khi đẩy lên production.
Giải pháp 3: Refactor sang Class Pattern hiện đại
Lựa chọn thông minh nhất là loại bỏ hoàn toàn componentWillMount. Hầu hết logic nằm ở đó thực tế nên thuộc về constructor hoặc componentDidMount.
Trường hợp A: Khởi tạo State
Nếu bạn đã sử dụng componentWillMount để thiết lập state dựa trên props, hãy chuyển logic đó vào constructor. Điều này đảm bảo state đã sẵn sàng trước khi lần render đầu tiên diễn ra.
// Tránh cách này
componentWillMount() {
this.setState({ user: this.props.defaultUser });
}
// Nên làm cách này thay thế
constructor(props) {
super(props);
this.state = {
user: props.defaultUser
};
}
Trường hợp B: Gọi API và Subscriptions
Chuyển mọi hoạt động lấy dữ liệu (data fetching) hoặc các event listener sang componentDidMount. Trong khi componentWillMount chạy trước lần render đầu tiên, thì componentDidMount chạy ngay sau đó. Trong 99% trường hợp, sự khác biệt về thời gian này là không thể nhận thấy đối với người dùng, và nó an toàn hơn đáng kể cho việc render phía server (SSR).
// Tránh cách này
componentWillMount() {
axios.get('/api/data').then(res => this.setState({ data: res.data }));
}
// Nên làm cách này thay thế
componentDidMount() {
axios.get('/api/data').then(res => this.setState({ data: res.data }));
}
Giải pháp 4: Tiêu chuẩn vàng (Hooks)
Nếu bạn đã chuẩn bị refactor, hãy chuyển đổi class sang Functional Component. Đây là con đường bền vững nhất cho tương lai. Hooks không có phương thức tương đương trực tiếp với 'Will Mount' vì chính thân hàm (function body) sẽ xử lý giai đoạn đó.
import React, { useState, useEffect } from 'react';
const MyComponent = ({ defaultUser }) => {
// Khởi tạo State thay thế cho constructor
const [user] = useState(defaultUser);
const [data, setData] = useState(null);
useEffect(() => {
// Cái này thay thế cho componentDidMount
const fetchData = async () => {
const res = await axios.get('/api/data');
setData(res.data);
};
fetchData();
}, []); // Mảng rỗng đảm bảo code này chỉ chạy một lần
return <div>{user.name}</div>;
};
Danh sách kiểm tra
Sau khi áp dụng các thay đổi, hãy thực hiện các bước kiểm tra sau:
- Tải lại ứng dụng và xác nhận thông báo
Warning: componentWillMount has been renamedđã biến mất. - Mở tab Network trong DevTools. Đảm bảo các cuộc gọi API không bị kích hoạt hai lần một cách bất thường.
- Chú ý hiện tượng "flash" (nháy) của state trống. Nếu việc chuyển logic sang
componentDidMountgây ra hiện tượng nhấp nháy, hãy thêm một loading spinner đơn giản để lấp đầy khoảng trống đó.
Những lưu ý quan trọng
- Dừng các side effects trước khi render: Không bao giờ thay đổi state hoặc kích hoạt các hành động bên ngoài trước khi component mount.
- Cập nhật hệ sinh thái của bạn: Cảnh báo này thường bắt nguồn từ các phiên bản cũ của
react-routerhoặcmaterial-ui. Giữ cho các dependency luôn mới là đã thắng được một nửa trận chiến. - Hooks là tương lai: React đang rời xa class lifecycles. Các tính năng mới như Concurrent Mode và Suspense được thiết kế đặc biệt cho Functional Components.

