React đưa khái niệm UI hướng thành phần, render khai báo và view điều khiển bằng state—khiến các đội chuyển từ code theo trang sang hệ thống tái sử dụng và pattern có thể mở rộng.

React không chỉ đưa ra một thư viện mới—nó thay đổi cả ý nghĩa khi các đội nói “kiến trúc frontend”. Về mặt thực tế, kiến trúc frontend là tập các quyết định giúp mã UI dễ hiểu ở quy mô lớn: bạn tách UI thành các phần thế nào, dữ liệu di chuyển giữa chúng ra sao, state nằm ở đâu, xử lý side effect (như gọi dữ liệu) thế nào, và làm sao để kết quả có thể kiểm thử và nhất quán trong cả đội.
Tư duy component là coi mỗi mảnh UI là một đơn vị nhỏ, có thể tái sử dụng, tự chịu trách nhiệm cho việc render và có thể ghép với các đơn vị khác để tạo ra trang hoàn chỉnh.
Trước khi React phổ biến, nhiều dự án tổ chức quanh các trang và thao tác DOM: “tìm phần tử này, đổi text, bật/tắt class này.” React khiến các đội chuyển sang một mặc định khác:
Những ý tưởng này thay đổi công việc hằng ngày. Khi review code, người ta bắt đầu hỏi “state này thuộc về đâu?” thay vì “bạn dùng selector nào?” Thiết kế và kỹ sư có thể thống nhất ngôn ngữ component, và đội có thể mở rộng thư viện các khối xây dựng UI mà không phải viết lại toàn bộ trang.
Ngay cả khi một đội sau này chuyển sang framework khác, nhiều thói quen do React hình thành vẫn còn: kiến trúc hướng thành phần, render khai báo, luồng dữ liệu có thể dự đoán, và ưu tiên cho component của hệ thống thiết kế hơn là code trang đơn lẻ. React làm cho những pattern này trở nên bình thường—và ảnh hưởng tới hệ sinh thái frontend rộng hơn.
Trước React, nhiều đội xây giao diện quanh các trang, không phải đơn vị UI tái sử dụng. Một cấu trúc phổ biến là template render phía server (PHP, Rails, Django, JSP, v.v.) tạo ra HTML, kèm theo jQuery để thêm tính tương tác.
Bạn render trang, rồi “kích hoạt” nó bằng script: datepicker, modal plugin, validator, carousel—mỗi cái có kỳ vọng markup và hook sự kiện riêng.
Code thường trông như: tìm node DOM, gắn handler, thay đổi DOM, và hy vọng không có gì vỡ. Khi UI lớn dần, “nguồn chân lý” thầm lặng trở thành chính DOM.
Hành vi UI hiếm khi nằm ở một chỗ. Nó bị chia giữa:
Một widget đơn lẻ—ví dụ tóm tắt checkout—có thể được dựng một phần phía server, cập nhật một phần bằng AJAX, và điều khiển một phần bằng plugin.
Cách làm này ổn cho cải tiến nhỏ, nhưng gây ra các vấn đề lặp lại:
Các framework như Backbone, AngularJS, và Ember cố gắng mang lại cấu trúc với model, view, routing—thường cải thiện nhiều. Nhưng nhiều đội vẫn trộn pattern, tạo ra khoảng trống cho cách tiếp cận đơn giản hơn để xây UI như các đơn vị có thể lặp lại.
Điểm chuyển quan trọng nhất của React nói dễ dàng nhưng có sức mạnh thực tiễn: UI là một hàm của state. Thay vì coi DOM là “nguồn chân lý” và giữ nó đồng bộ thủ công, bạn coi dữ liệu là nguồn chân lý và để UI là kết quả.
State chỉ là dữ liệu hiện tại mà màn hình phụ thuộc: menu đang mở hay đóng, nội dung nhập trong form, mục trong danh sách, bộ lọc đang chọn. Khi state thay đổi, bạn không phải dò khắp trang để cập nhật nhiều node DOM. Bạn cập nhật state, và UI render lại cho phù hợp.
Code kiểu DOM-first thường có logic cập nhật phân tán:
Với mô hình React, những “cập nhật” đó trở thành điều kiện trong output render. Màn hình trở thành mô tả dễ đọc về những gì nên hiển thị cho một state nhất định.
function ShoppingList() {
const [items, setItems] = useState([]);
const [text, setText] = useState("");
const add = () => setItems([...items, text.trim()]).then(() => setText(""));
return (
<section>
<form onSubmit={(e) => { e.preventDefault(); add(); }}>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button disabled={!text.trim()}>Add</button>
</form>
{items.length === 0 ? <p>No items yet.</p> : (
<ul>{items.map((x, i) => <li key={i}>{x}</li>)}</ul>
)}
</section>
);
}
Lưu ý cách thông báo rỗng, trạng thái disabled của nút, và nội dung danh sách đều được suy ra từ items và text. Đó là lợi ích kiến trúc: hình dạng dữ liệu và cấu trúc UI khớp nhau, khiến màn hình dễ lý giải, kiểm thử và phát triển.
React biến “component” thành đơn vị mặc định của công việc UI: một mảnh nhỏ, có thể tái sử dụng, gom markup, hành vi, và móc styling sau giao diện rõ ràng.
Thay vì rải template HTML, listener và selector CSS khắp nơi, component giữ các phần chuyển động lại gần nhau. Điều này không có nghĩa mọi thứ phải ở trong một file—nhưng có nghĩa code được tổ chức quanh những gì người dùng thấy và làm, chứ không phải API DOM.
Một component thực tế thường bao gồm:
Thay đổi quan trọng là bạn không còn nghĩ theo kiểu “cập nhật div này” mà nghĩ “render Button trong trạng thái disabled”.
Khi một component lộ ra một tập props nhỏ (inputs) và sự kiện/callback (outputs), sẽ dễ thay đổi bên trong mà không phá phần còn lại của app. Đội có thể sở hữu component hoặc thư mục cụ thể (ví dụ “checkout UI”) và cải thiện chúng an toàn.
Đóng gói cũng giảm coupling vô ý: ít selector toàn cục, ít side effect cross-file, ít bất ngờ “tại sao handler click này ngừng hoạt động?”.
Khi component trở thành khối xây dựng chính, code bắt đầu phản chiếu sản phẩm:
Sự tương ứng này làm các cuộc thảo luận UI dễ dàng hơn: designer, PM và kỹ sư nói về cùng “đối tượng”.
Tư duy component khiến nhiều codebase chuyển sang tổ chức theo feature hoặc domain (ví dụ /checkout/components/CheckoutForm) và thư viện UI chia sẻ (thường /ui/Button). Cấu trúc đó mở rộng tốt hơn so với folder chỉ theo trang khi tính năng tăng lên và chuẩn bị cho hệ thống thiết kế.
Phong cách render của React thường được gọi là khai báo, nghĩa là: bạn mô tả giao diện mong muốn cho một tình huống, và React lo việc làm cho trình duyệt khớp.
Trong cách tiếp cận DOM-first cũ, bạn thường viết từng bước:
Với render khai báo, bạn mô tả kết quả:
Nếu người dùng đã đăng nhập thì hiện tên họ. Nếu chưa thì hiện nút “Sign in”.
Sự chuyển đổi này giảm lượng “ghi chép” UI bạn phải làm. Bạn không liên tục theo dõi phần tử nào tồn tại và cần cập nhật—bạn tập trung vào các trạng thái ứng dụng có thể có.
JSX là cách tiện lợi để viết cấu trúc UI gần với logic điều khiển nó. Thay vì tách “file template” và “file logic” rồi nhảy qua lại, bạn giữ các phần liên quan cùng chỗ: markup-like structure, điều kiện, vài quyết định format và handlers.
Sự đồng vị này là lý do lớn khiến mô hình component của React thực tế và dễ dùng. Một component không chỉ là một đoạn HTML hay một gói JavaScript—nó là một đơn vị hành vi UI.
Mối bận tâm thông thường là JSX trộn HTML và JavaScript, nghe như bước lùi. Nhưng JSX không thực sự là HTML—nó là cú pháp tạo ra các gọi JavaScript. Quan trọng hơn, React không pha trộn công nghệ mà gom những thứ thay đổi cùng nhau.
Khi logic và cấu trúc UI liên kết chặt (ví dụ: “hiện thông báo lỗi chỉ khi validate fail”), giữ chúng cùng chỗ thường rõ ràng hơn là rải rác khắp file.
JSX làm React dễ tiếp cận, nhưng khái niệm cơ bản vượt ra ngoài JSX. Bạn có thể viết React không dùng JSX, và các framework khác cũng dùng render khai báo với cú pháp template khác. Tác động lâu dài là tư duy: coi UI như hàm của state, và để framework lo cơ chế giữ màn hình đồng bộ.
Trước React, lỗi thường xuất phát từ chuyện đơn giản: dữ liệu thay đổi mà UI không thay đổi. Devs sẽ fetch dữ liệu mới, rồi thủ công tìm node DOM thích hợp, đổi text, bật/tắt class, thêm/xóa phần tử, và giữ tất cả nhất quán qua các trường hợp cạnh. Dần dần, logic cập nhật phức tạp hơn cả UI.
Thay đổi lớn trong workflow của React là bạn không ra lệnh cho trình duyệt cách thay đổi trang. Bạn mô tả giao diện cho một state, và React tính toán cách cập nhật DOM thật để khớp.
Reconciliation là quá trình React so sánh lần render trước với lần render hiện tại, rồi áp dụng tập thay đổi nhỏ nhất lên DOM thật.
Điểm quan trọng không phải React dùng “Virtual DOM” như mẹo hiệu năng. Mà là React đem lại mô hình có thể dự đoán:
Sự nhất quán đó cải thiện workflow dev: ít cập nhật DOM thủ công, ít trạng thái không nhất quán, và cập nhật UI theo cùng quy tắc xuyên ứng dụng.
Khi render danh sách, React cần cách ổn định để ghép “phần tử cũ” với “phần tử mới” trong quá trình reconciliation. Đó là mục đích của key.
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
Dùng key ổn định và duy nhất (như ID). Tránh dùng index mảng khi mục có thể đổi chỗ/chèn/xóa—nếu không React có thể tái sử dụng instance component sai, dẫn đến hành vi UI bất ngờ (ví dụ input giữ giá trị sai).
React đã định nghĩa lại kiến trúc frontend quanh một vài quyết định cốt lõi:
Hiệu quả thực tế là giảm việc quản lý DOM thủ công và tạo ra ranh giới rõ ràng cho đội và công cụ.
Component thinking nghĩa là coi mỗi phần UI như một đơn vị nhỏ, có thể tái sử dụng, tự chịu trách nhiệm cho việc render của nó và có thể ghép vào các màn hình lớn hơn. Thực tế, một component thường gom:
Điều này biến công việc từ “cập nhật thẻ DOM này” thành “render component này cho trạng thái này”.
Trong code theo kiểu DOM-first, DOM thường trở thành nguồn chân lý, nên bạn phải đồng bộ thủ công nhiều phần tử. Trong React, bạn cập nhật state và render dựa trên state đó, nên các điều kiện như spinner, nút bị vô hiệu, và trạng thái rỗng tự nhiên giữ được nhất quán.
Một phép thử tốt: nếu bạn thường xuyên viết nhiều bước “tìm phần tử và bật/tắt class”, bạn đang chống lại mô hình; nếu UI ra khỏi trạng thái, thường đó là vấn đề quyền sở hữu state.
Trước React, nhiều ứng dụng thiên về page: template render phía server cộng jQuery và plugin. Hành vi phân tán giữa server views, thuộc tính HTML và initializer JS.
Vấn đề thường gặp:
React thúc đẩy đội về component tái sử dụng và cập nhật có dự đoán được.
Giao diện khai báo nghĩa là mô tả kết quả giao diện cho một trạng thái nhất định, không phải cách thay đổi DOM theo từng bước.
Thay vì:
Bạn biểu đạt điều kiện trong output render (ví dụ “nếu đã đăng nhập thì hiện tên, nếu chưa thì hiện nút ‘Sign in’”), và React xử lý việc cập nhật DOM thật.
JSX giúp gom cấu trúc UI gần logic điều khiển (điều kiện, format, handlers). Điều này giảm việc phải chuyển giữa file template và file logic.
JSX không phải HTML; nó biên dịch thành các gọi JavaScript. Lợi ích chính là tổ chức: gom những thứ cùng thay đổi (UI + hành vi) vào một chỗ thường dễ duy trì hơn.
Reconciliation là quá trình React so sánh output render trước đó với lần render mới, rồi áp dụng tập thay đổi nhỏ nhất lên DOM.
Bài học thực tế: bạn viết logic render như thể xây lại UI từ đầu, và React cập nhật từng phần để người dùng không phải chịu chi phí rebuild hoàn toàn.
Với danh sách, hãy dùng key ổn định và duy nhất (ví dụ id). Tránh dùng chỉ số mảng khi phần tử có thể sắp xếp/chèn/xóa, vì React có thể tái sử dụng nhầm instance component (ví dụ input giữ giá trị sai).
Luồng dữ liệu một chiều nghĩa là dữ liệu truyền từ cha xuống con qua props, trong khi con yêu cầu thay đổi bằng callback.
Điều này làm ranh giới rõ ràng:
Khi debug, thường bạn chỉ cần tìm nơi state được giữ — thay vì lần theo một mạng lưới mutation ẩn.
Composition nghĩa là ghép các component nhỏ để tạo hành vi, thay vì dùng cây kế thừa lớp. Một số pattern phổ biến:
Hướng tiếp cận thực tế:
Chọn theo độ phức tạp ứng dụng và nhu cầu đội, đừng chạy theo trào lưu.
childrenRequireAuth hay ErrorBoundaryfooter, emptyState, renderRow) khi children không đủCách này giữ tính linh hoạt mà tránh cây kế thừa phức tạp và các hiệu ứng lan truyền từ base class.