Sửa lỗi: ReactDOM.render đã bị ngừng hỗ trợ trong React 18

beginner⚛️ React2026-04-25| React 18.x, Node.js 14.x trở lên, Webpack/Vite/Create React App

Error Message

Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17.
#react#react18#lap-trinh-web#javascript

Vấn đề

Nếu bạn vừa cập nhật package.json lên React 18, bảng điều khiển trình duyệt của bạn có thể đang hiển thị cảnh báo. Ứng dụng vẫn tải, nhưng nó đang chạy ở "Chế độ Cũ" (Legacy Mode). Điều này có nghĩa là bạn đang bỏ lỡ những tính năng tốt nhất của React 18. Bằng cách giữ lại API cũ, bạn sẽ mất đi các lợi ích như Automatic Batching, vốn có thể giảm số lần render lại lên đến 50% trong các trình xử lý sự kiện phức tạp.

Đây là thông báo chính xác mà bạn đang thấy:

Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17.

React 18 đã thay đổi cách khởi tạo root của ứng dụng. API ReactDOM.render cũ đã chính thức bị ngừng hỗ trợ (deprecated). Nó đã được thay thế bằng createRoot để kích hoạt trình render đồng thời (concurrent renderer) mới và cải thiện khả năng phản hồi của giao diện người dùng.

Cách khắc phục lỗi

Việc khắc phục lỗi này yêu cầu bạn cấu trúc lại tệp đầu vào (entry file) — thường là index.js hoặc main.jsx — trong khoảng hai phút. Hãy làm theo các bước sau để đưa ứng dụng của bạn hoạt động bình thường trở lại.

Bước 1: Cập nhật các bản Import

Thay đổi đầu tiên nằm ở nơi bạn lấy các công cụ. Trong React 17, bạn import từ react-dom. Trong React 18, hàm createRoot nằm trong một đường dẫn con cụ thể.

Import cũ:

import ReactDOM from 'react-dom';

Import mới:

import { createRoot } from 'react-dom/client';

Bước 2: Chuyển sang createRoot

Trước đây, chúng ta truyền component và phần tử DOM vào một hàm duy nhất. React 18 chia quá trình này thành hai bước: tạo root và sau đó render component.

Cách cũ (React 17):

const container = document.getElementById('root');
ReactDOM.render(<App />, container);

Cách mới (React 18):

const container = document.getElementById('root');
const root = createRoot(container);
root.render(<App />);

Bước 3: Giải quyết kiểm tra Null trong TypeScript

Người dùng TypeScript có thể thấy lỗi thông báo rằng container có thể bị null. Bạn phải xác minh phần tử tồn tại trước khi khởi tạo root. Điều này ngăn chặn lỗi runtime nếu tệp index.html của bạn thiếu ID mong muốn.

import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');

if (container) {
  const root = createRoot(container);
  root.render(<App />);
}

Nếu bạn chắc chắn 100% rằng phần tử đó tồn tại, hãy sử dụng toán tử khẳng định không null (non-null assertion operator):

const root = createRoot(document.getElementById('root')!);

Các thay đổi đối với Server-Side Rendering (SSR)

Bạn có đang sử dụng SSR với cấu hình Express tùy chỉnh không? Nếu có, ReactDOM.hydrate cũng đang dần bị loại bỏ. Bạn nên chuyển sang hydrateRoot từ gói react-dom/client để giữ cho logic hydration tương thích với trình render mới.

Trước:

ReactDOM.hydrate(<App />, document.getElementById('root'));

Sau:

import { hydrateRoot } from 'react-dom/client';

const container = document.getElementById('root');
const root = hydrateRoot(container, <App />);

Xác minh bản sửa lỗi

Sau khi thay đổi mã, hãy thực hiện ba bước kiểm tra nhanh sau:

  • Kiểm tra Console: Mở DevTools (F12). Cảnh báo sẽ biến mất ngay lập tức sau khi tải lại trang.
  • React DevTools: Tiện ích mở rộng bây giờ sẽ gắn nhãn ứng dụng của bạn là đang sử dụng "Concurrent Mode".
  • Kiểm tra State: Kích hoạt một vài cập nhật state liên tục. Bạn sẽ nhận thấy hiệu suất mượt mà hơn nhờ logic automatic batching mới.

Các lỗi thường gặp

1. Lồng createRoot

Tuyệt đối không gọi createRoot bên trong một component. Nó chỉ thuộc về tệp đầu vào của bạn. Việc gọi nó bên trong một functional component sẽ tạo lại toàn bộ cây ứng dụng mỗi khi render lại, gây ảnh hưởng nghiêm trọng đến hiệu suất và xóa sạch state cục bộ của bạn.

2. Thiếu hậu tố /client

Nếu bạn quên hậu tố /client trong lệnh import, hàm này có khả năng sẽ bị undefined. Đây là lý do phổ biến nhất gây ra lỗi "createRoot is not a function".

3. Callback đã bị loại bỏ

Callback của ReactDOM.render (đối số thứ ba) đã không còn nữa. Nếu bạn cần chạy mã sau khi mount lần đầu, hãy sử dụng hook useEffect bên trong component App cấp cao nhất của bạn.

Giải pháp thay thế hiện đại:

function App() {
  useEffect(() => {
    console.log('Ứng dụng đã mount thành công!');
  }, []);

  return <div>Ứng dụng React 18 của tôi</div>;
}

Related Error Notes