Home / Frontend / Lịch Sử React: Từ Version 15 Đến React 19 Qua Từng Cột Mốc
Frontend

Lịch Sử React: Từ Version 15 Đến React 19 Qua Từng Cột Mốc

Dòng thời gian React từ version 15 tới 19: Fiber, Hooks, Concurrent, Actions — mỗi cột mốc ra đời để giải quyết gì.

React đi từ version 15 (2016) tới 19 — mỗi bản mang một ý tưởng lớn: Fiber, Hooks, Concurrent, Actions. Hiểu dòng thời gian này giúp bạn đọc code cũ (vì sao vẫn còn componentWillMount?), chọn pattern mới cho đúng, và biết cái gì đã deprecated. Bài này là bản đồ tổng quan: mỗi cột mốc tóm tắt ngắn rồi dẫn tới các bài đi sâu, thay vì lặp lại chi tiết từng cơ chế ở đây.

1. Bản đồ tổng quan trong một hình

Trước khi đi vào từng bản, hãy nhìn cả dòng thời gian. Bảy cột mốc dưới đây không phải là danh sách release đầy đủ, mà là những bản đổi cách bạn viết React — mỗi bản giải quyết một vấn đề mà bản trước để lại.

View Mermaid diagram code
flowchart LR
    V15["15 · 2016<br/>SFC + tách react-dom"] --> V16["16 · 2017<br/>Fiber"]
    V16 --> V163["16.3 · 2018<br/>Context, refs"]
    V163 --> V168["16.8 · 2019<br/>Hooks"]
    V168 --> V17["17 · 2020<br/>Gradual upgrade"]
    V17 --> V18["18 · 2022<br/>Concurrent"]
    V18 --> V19["19 · 2024<br/>Actions, use()"]

    style V15 fill:#0e2233,stroke:#4aa8ff,stroke-width:1.5px,color:#9ecbff
    style V16 fill:#241832,stroke:#c08cff,stroke-width:1.5px,color:#d4b3ff
    style V163 fill:#0e2233,stroke:#4aa8ff,stroke-width:1.5px,color:#9ecbff
    style V168 fill:#1a2c08,stroke:#c7ff50,stroke-width:1.5px,color:#c7ff50
    style V17 fill:#0e2233,stroke:#4aa8ff,stroke-width:1.5px,color:#9ecbff
    style V18 fill:#241832,stroke:#c08cff,stroke-width:1.5px,color:#d4b3ff
    style V19 fill:#1a2c08,stroke:#c7ff50,stroke-width:1.5px,color:#c7ff50

Hai cột mốc tô xanh lá là hai bước ngoặt lớn nhất về cách viết component: Hooks (16.8) và Actions (19). Hai cột tím — Fiber (16) và Concurrent (18) — là hai lần React viết lại phần lõi bên dưới. Còn lại là những bản dọn dẹp và chuẩn bị nền. Giờ đi qua từng cột.

2. React 15 — kỷ nguyên trước Fiber

React 15 (2016) là bản ổn định cuối cùng trước khi phần lõi được viết lại. Hai đặc điểm định hình thời kỳ này thực ra xuất hiện từ 0.14 và chín muồi ở đây: stateless functional component và việc tách react-dom khỏi react.

Trước đó bạn phải viết mọi component bằng React.createClass hoặc class. Với component chỉ nhận props và render ra UI, cú pháp function gọn hơn hẳn:

1
2
3
4
// React 15: stateless functional component — chỉ nhận props, không có state
function Welcome({ name }) {
  return <h1>Xin chào, {name}</h1>;
}

Việc tách package cũng quan trọng không kém: react giữ phần định nghĩa component (platform-agnostic), còn react-dom lo việc render xuống DOM của trình duyệt. Nhờ ranh giới này mà sau đó React Native, và các renderer khác, dùng chung react mà không kéo theo DOM. Đây là nền để mọi thứ sau này mở rộng — nhưng bản thân React 15 vẫn còn một điểm yếu lớn: reconciler chạy đồng bộ, đệ quy, không thể ngắt giữa chừng.

3. React 16 — Fiber viết lại reconciler

React 16 (2017) thay toàn bộ reconciler bằng kiến trúc mới tên Fiber. Điểm cốt lõi: quá trình so sánh cây (reconciliation) được chia thành các đơn vị công việc nhỏ có thể tạm dừng, ưu tiên và tiếp tục — nền tảng cho concurrent rendering nhiều năm sau. Ở version 16 người dùng chưa bật được concurrent, nhưng phần lõi đã sẵn sàng.

Cùng với Fiber, 16 mở ra loạt tính năng mà trước đó không làm được:

  • Error boundaries — component bắt lỗi render của cây con bằng componentDidCatch, thay vì để cả app trắng màn hình.
  • Portals — render một node ra ngoài cây DOM cha (modal, tooltip) qua ReactDOM.createPortal.
  • Fragments (từ 16.2) — trả về nhiều phần tử mà không cần thẻ bọc thừa.
  • Trả về array/string trực tiếp từ render, không bắt buộc một root element.
1
2
3
4
5
6
7
8
9
// React 16.2+: Fragment — nhóm phần tử mà không thêm <div> thừa vào DOM
function Columns() {
  return (
    <>
      <td>Xin chào</td>
      <td>Thế giới</td>
    </>
  );
}

Fiber là lý do vì sao mọi khả năng nâng cao về sau — transitions, Suspense, tách nhỏ công việc render — đều khả thi. Nó không đổi cách bạn viết component, nhưng đổi cách React thực thi chúng.

4. React 16.3 — Context mới, refs, và bắt đầu dọn lifecycle

React 16.3 (2018) là bản “dọn API”. Nó thay Context API cũ (vốn khó dùng và dễ vỡ) bằng createContext, đồng thời chuẩn hoá cách làm việc với ref:

1
2
3
4
5
6
7
// React 16.3: Context API mới
const ThemeContext = React.createContext("light");

// createRef + forwardRef: chuyển ref xuống một DOM node bên trong
const FancyInput = React.forwardRef((props, ref) => (
  <input ref={ref} className="fancy" {...props} />
));

Bản này cũng giới thiệu StrictMode — một wrapper chỉ chạy ở development để cảnh báo các pattern rủi ro. Quan trọng hơn cho ai đọc code cũ: 16.3 bắt đầu deprecate ba lifecycle componentWillMount, componentWillReceiveProps, và componentWillUpdate. Chúng không an toàn khi render có thể bị ngắt (điều Fiber cho phép), nên React thêm tiền tố UNSAFE_ và khuyến khích thay bằng getDerivedStateFromProps. Nếu bạn thấy componentWillReceiveProps trong một codebase, đó là dấu hiệu code viết trước 2018. Chi tiết mapping lifecycle class sang cách nghĩ mới nằm ở bài Lifecycle React: Class Component vs Functional Component.

5. React 16.8 — Hooks, bước ngoặt lớn nhất

Nếu chỉ nhớ một cột mốc, hãy nhớ React 16.8 (2019): Hooks. Trước Hooks, muốn có state hay lifecycle thì phải viết class. Hooks cho phép function component — vốn chỉ render UI — nắm giữ state và side effect, xoá bỏ ranh giới “class cho logic, function cho hiển thị”.

1
2
3
4
5
6
7
8
9
10
11
// React 16.8: cùng một function component, giờ có state nhờ useState
import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Đã bấm {count} lần
    </button>
  );
}

useState, useEffect, useContext, useReducer… gói lại logic có state thành hàm tái sử dụng (custom hook), thứ mà HOC và render props trước đó làm rất rối. Từ 16.8 trở đi, code React mới gần như không còn viết class. Đây cũng là lý do phần lớn hệ sinh thái (thư viện, tài liệu) chuyển sang function component. Cách từng hook (useState, useEffect) thay thế đúng phương thức lifecycle nào của class được đối chiếu kỹ trong bài lifecycle nói trên.

6. React 17 — bản “không tính năng mới”

React 17 (2020) nổi tiếng vì tiêu đề release: “No New Features”. Nghe như một bản nhàm chán, nhưng mục tiêu của nó là làm cho việc nâng cấp dần (gradual upgrade) trở nên khả thi — cho phép hai version React chạy cạnh nhau trong cùng một trang, để đội lớn migrate từng phần thay vì nâng cả app một lần.

Để làm được điều đó, 17 đổi hai thứ ở tầng nền:

  • Event delegation gắn vào root container thay vì document. Trước 17, React gắn handler ở cấp document; giờ nó gắn vào đúng DOM node mà bạn render vào. Nhờ vậy nhiều root React (nhiều version) không xung đột với nhau.
  • JSX transform mới — bạn không cần import React from "react" chỉ để dùng JSX nữa. Trình biên dịch tự chèn lời gọi runtime.
1
2
3
4
// React 17+: không cần "import React" chỉ để viết JSX
function App() {
  return <h1>Không có dòng import React ở đầu file</h1>;
}

17 không thay đổi cách bạn viết component hằng ngày, nhưng nó chuẩn bị nền kỹ thuật để chuyển sang concurrent ở 18 mà không buộc mọi đội nâng cấp cùng lúc.

7. React 18 — concurrent rendering thành hiện thực

React 18 (2022) mở khoá phần “concurrent” mà Fiber đã chuẩn bị từ 2017. Bạn bật nó bằng cách đổi entry point sang createRoot:

1
2
3
4
5
// React 18: createRoot thay cho ReactDOM.render
import { createRoot } from "react-dom/client";

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

Đổi một dòng này mở ra nhiều thứ:

  • Automatic batching — nhiều lần setState trong cùng một tick (kể cả trong setTimeout, promise, hay native handler) gộp lại thành một lần re-render. Trước 18, batching chỉ xảy ra trong React event handler.
  • Transitions — đánh dấu update “không khẩn cấp” bằng startTransition/useTransition, để React ưu tiên giữ input mượt và có thể ngắt việc render nặng giữa chừng.
  • Suspense SSR streaming — server render theo dòng (renderToPipeableStream), gửi phần shell trước rồi stream nốt các mảnh khi sẵn sàng.

Automatic batching được mổ xẻ trong bài Batch Async Tasks Trong React: Từ setState Đến Debounce; còn transitions, useDeferredValue và cơ chế render ngắt được nằm ở bài Concurrency Trong React 18: Transitions Và Suspense. Ở đây chỉ cần nhớ: 18 là bản biến concurrent từ tiềm năng thành API dùng được.

8. React 19 — Actions và use()

React 19 (2024) đóng gói các pattern async lặp đi lặp lại thành API chính thức. Trọng tâm là Actions: một async function truyền thẳng vào action của <form>, còn React tự lo pending state, error và optimistic update qua useActionState, useOptimistic, useFormStatus.

1
2
3
4
// React 19: ref là prop thường, không cần forwardRef nữa
function MyInput({ placeholder, ref }) {
  return <input placeholder={placeholder} ref={ref} />;
}

Ba thay đổi đáng nhớ khác:

  • use() — đọc một promise (hoặc context) ngay trong render; React tự suspend cho tới khi promise resolve, và Suspense gần nhất hiển thị fallback.
1
2
3
4
5
6
7
// React 19: use() đọc promise ngay trong thân render
import { use } from "react";

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);
  return comments.map((c) => <p key={c.id}>{c.text}</p>);
}
  • ref là prop — function component nhận thẳng ref như một prop bình thường, không còn cần forwardRef cho code mới.
  • React Compiler — một công cụ build riêng (không phải API trong core) tự động memo hoá, giảm nhu cầu useMemo/useCallback thủ công; ở React 19 phần runtime của compiler được tích hợp sẵn (react/compiler-runtime).

Toàn bộ danh sách tính năng React 19, cùng cách Actions dựng trên nền concurrent, được đi sâu ở bài Tính Năng Mới Trong React 19: Actions Và Hook use().

Nhìn lại cả chặng, dòng chảy khá rõ: Fiber (16) dựng nền, Hooks (16.8) đổi cách viết logic, Concurrent (18) khai thác cái nền đó, và Actions (19) gói các pattern async thành API sẵn dùng. Khi bạn gặp một đoạn code React lạ, hãy đối chiếu nó với bản đồ này — componentWillMount là trước 2018, class extends Component là trước 2019, ReactDOM.render là trước 2022 — và bạn sẽ biết ngay nên giữ, nên thay, hay nên viết lại theo pattern nào.

Table of Contents
  1. 1. Bản đồ tổng quan trong một hình
  2. 2. React 15 — kỷ nguyên trước Fiber
  3. 3. React 16 — Fiber viết lại reconciler
  4. 4. React 16.3 — Context mới, refs, và bắt đầu dọn lifecycle
  5. 5. React 16.8 — Hooks, bước ngoặt lớn nhất
  6. 6. React 17 — bản “không tính năng mới”
  7. 7. React 18 — concurrent rendering thành hiện thực
  8. 8. React 19 — Actions và use()