5 phút

React đã định hình lại kiến trúc frontend bằng các component

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 đã định hình lại kiến trúc frontend bằng các component

Những thay đổi khi React xuất hiện

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, trong một câu

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.

Những chuyển dịch React khuyến khích

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:

  • UI điều khiển bằng state: bạn cập nhật state, và UI tự động cập nhật theo.
  • Composition hơn là nối dây: bạn lắp ráp màn hình bằng cách lồng và kết hợp component thay vì ghép hành vi qua các file rời rạc.
  • Tái sử dụng là ưu tiên: mẫu UI chung trở thành component thay vì sao chép markup.

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.

Lớn hơn React

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: UI theo trang và code ưu tiên DOM

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.

Stack điển hình: template + jQuery + plugin

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 phân tán trên nhiều lớp

Hành vi UI hiếm khi nằm ở một chỗ. Nó bị chia giữa:

  • View phía server (render điều kiện, partial, feature flag)
  • HTML (thuộc tính data-*, handler inline, field ẩn)
  • JavaScript (selector jQuery, state toàn cục, khởi tạo plugin)

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.

Những vấn đề thường gặp

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:

  • UI bị nhân bản: cùng “component” được dựng nhiều nơi trong template và trang
  • State không nhất quán: spinner, nút bị disable, thông báo lỗi bị lệch nhịp
  • Code DOM dễ vỡ: thay đổi nhỏ trong markup phá vỡ selector và wiring sự kiện

MV* ban đầu như một cầu nố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.

Ý tưởng lớn: UI là hàm của state

Đ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” nghĩa là gì trong ứng dụng hàng ngày

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.

Tại sao điều này giảm thao tác DOM thủ công

Code kiểu DOM-first thường có logic cập nhật phân tán:

  • Thay checkbox → cập nhật label
  • Thêm mục → cập nhật danh sách và thông báo trạng thái rỗng
  • Submit form → disable button, hiện spinner, xử lý lỗi

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.

Một ví dụ nhỏ dễ hiểu

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ừ itemstext. Đó 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.

Component là khối xây dựng mới

Mở rộng khi ứng dụng phát triển
Chọn free, pro, business, hoặc enterprise tùy theo cách đội bạn xây dựng và đưa sản phẩm ra.

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.

Component thực chất là gì

Một component thực tế thường bao gồm:

  • Cấu trúc (những gì nó render)
  • Tương tác (handlers, state, effects)
  • Móc styling (class name, variant, token)

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”.

Đóng gói = dễ bảo trì và rõ quyền sở hữu

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?”.

Component phản ánh khái niệm sản phẩm

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:

  • Button (primary/secondary, loading, icon)
  • Modal (open/close, focus trap, escape key)
  • CheckoutForm (validate, submit, error states)

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”.

Ảnh hưởng tới cấu trúc file

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ế.

Render khai báo và lý do JSX giúp nó phổ biến

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.

“Mô tả kết quả, không các bước”

Trong cách tiếp cận DOM-first cũ, bạn thường viết từng bước:

  • tìm phần tử
  • tạo node mới
  • đặt text
  • append
  • sau đó cập nhật hoặc xóa

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ó.

Tại sao JSX giúp nó lan tỏa

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.

“Có phải đang trộn HTML và JS sao?”

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.

Render khai báo không chỉ có JSX

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ộ.

Reconciliation và Virtual DOM (không cần những huyền thoại)

Lập kế hoạch component rõ ràng
Dùng Chế độ Lập kế hoạch để phác thảo component, quyền sở hữu trạng thái và luồng dữ liệu trước khi sinh mã.

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 thực sự nghĩa là gì

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:

  • Bạn viết logic render như thể dựng lại UI từ đầu.
  • React cập nhật DOM dần dần để người dùng không phải chịu chi phí dựng lại toàn bộ.

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.

Keys: bài học thực tiễn

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).

Câu hỏi thường gặp

“Kiến trúc frontend” nghĩa là gì trong ngữ cảnh React?

React đã định nghĩa lại kiến trúc frontend quanh một vài quyết định cốt lõi:

  • tách UI thành các component tái sử dụng
  • làm cho giao diện dựa trên trạng thái (dữ liệu là nguồn chân lý)
  • dùng composition để xây màn hình
  • áp dụng luồng dữ liệu một chiều để làm rõ quyền sở hữu

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” là gì, định nghĩa ngắn gọn?

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:

  • cấu trúc (những gì nó render)
  • tương tác (handlers, state, effects)
  • móc cho styling (class/variant/token)

Đ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”.

Tại sao React giảm nhu cầu thao tác DOM thủ công?

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.

Những điểm đau lớn nhất của kiến trúc trước React, theo kiểu DOM-first là gì?

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:

  • UI bị nhân bản qua nhiều template/trang
  • selector dễ vỡ khi markup thay đổi
  • trạng thái UI không nhất quán (spinner/lỗi/nút lệch nhịp)

React thúc đẩy đội về component tái sử dụng và cập nhật có dự đoán được.

“Declarative rendering” nghĩa là gì và tại sao quan trọng?

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ì:

  • tạo node → đặt text → append → sau đó xóa

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.

Tại sao JSX giúp mô hình của React lan rộng?

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 của React là gì, và ý nghĩa thực tế của `key` là gì?

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 đơn giản hóa kiến trúc React thế nào?

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:

  • state nằm ở component sở hữu (nguồn chân lý)
  • props là input chỉ đọc cho component con

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 over inheritance” trông như thế nào trong dự án React thực tế?

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:

  • các layout component nhận children (PageShell, Grid, Card)
  • wrapper như RequireAuth hay ErrorBoundary
  • props kiểu slot (footer, 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.

Đội nên tiếp cận quản lý trạng thái thế nào khi app React lớn dần?

Hướng tiếp cận thực tế:

  1. State cục bộ cho UI cụ thể component (input, toggle, spinner)
  2. Lifting state up khi các sibling cần phối hợp
  3. Context cho các concern ngang (theme, locale, user hiện tại)
  4. Store ngoài (mô hình kiểu Redux) khi nhiều component xa xôi cần cùng dữ liệu và cần quy tắc cập nhật rõ ràng

Chọn theo độ phức tạp ứng dụng và nhu cầu đội, đừng chạy theo trào lưu.

Related posts