Render và Commit
Trước khi các component của bạn được hiển thị trên màn hình, chúng sẽ được render bởi React. Việc hiểu được các bước trong quá trình này sẽ giúp bạn suy nghĩ về cách mà code của bạn thực thi và giải thích được hành vi của nó.
You will learn
- Render nghĩa là gì trong React
- Khi nào và tại sao React render một component
- Các bước liên quan tới việc hiển thị một component lên màn hình
- Tại sao render không phải lúc nào cũng sinh ra một lần cập nhật DOM
Hãy tưởng tượng rằng các component của bạn là các đầu bếp đang chế biến các món ăn ngon bằng nguyên liệu trong nhà bếp. Trong ngữ cảnh này, React là người bồi bàn nhận các yêu cầu gọi món từ khách hàng và phục vụ các món ăn cho họ. Quy trình yêu cầu và phục vụ giao diện sẽ gồm 3 bước:
- Trigger một lần render (chuyển yêu cầu gọi món của khách tới nhà bếp)
- Render một component (chuẩn bị các món ăn bên trong nhà bếp)
- Commit vào DOM (bày các món ăn lên bàn)
Illustrated by Rachel Lee Nabors
Bước 1: Trigger một lần render
Có hai lý do khiến một component phải render:
- Đó là lần render khởi tạo của component đó.
- State của component đó (hoặc của các component bọc ngoài nó) bị thay đổi.
Render khởi tạo
Khi ứng dụng của bạn khởi chạy, bạn cần phải trigger một lần render khởi tạo. Các framework và sandbox đôi khi thường ẩn đoạn code này đi, nhưng nó được thực hiện bằng cách gọi hàm createRoot
cùng với DOM node, sau đó gọi phương thức render
cùng component của bạn:
import Image from './Image.js'; import { createRoot } from 'react-dom/client'; const root = createRoot(document.getElementById('root')) root.render(<Image />);
Thử comment dòng gọi root.render()
lại và bạn sẽ thấy component đó biến mất!
Render lại khi state cập nhật
Khi component đã được render khởi tạo, bạn có thể trigger thêm nhiều lần render khác bằng cách cập nhật lại state của nó bằng set
function. Việc cập nhật lại state cho component của bạn sẽ tự động yêu cầu một lần render. (Bạn có thể tưởng tượng những thứ này giống như việc một vị khách trong nhà hàng đang gọi thêm trà, món tráng miệng và các món khác sau lần gọi món đầu tiên, tùy thuộc vào trạng thái đói hay khát của họ).
Illustrated by Rachel Lee Nabors
Bước 2: React render các component của bạn
Sau khi bạn trigger một lần render, React sẽ gọi tới các component của bạn để xác định cái gì cần hiển thị lên màn hình. “Rendering” nghĩa là React đang gọi tới các component của bạn.
- Trong lần render khởi tạo, React sẽ gọi tới root component.
- Trong các lần render tiếp theo, React sẽ gọi tới function component có chứa state bị thay đổi và trigger việc render.
Quá trình này là một vòng lặp đệ quy: nếu component được cập nhật trả về component thì React tiếp theo sẽ render component đó, và nếu component đó lại cũng trả về một component khác thì React sẽ lại tiếp tục render component và cứ thế. Quá trình này sẽ kéo dài cho tới khi không còn component lồng nhau nào nữa và React biết chính xác cái gì sẽ được hiển thị lên màn hình.
Trong ví dụ phía dưới, React sẽ gọi tới Gallery()
và Image()
nhiều lần:
export default function Gallery() { return ( <section> <h1>Inspiring Sculptures</h1> <Image /> <Image /> <Image /> </section> ); } function Image() { return ( <img src="https://i.imgur.com/ZF6s192.jpg" alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals" /> ); }
- Trong lần render khởi tạo, React sẽ tạo ra các DOM node cho các thẻ
<section>
,<h1>
, and ba thẻ<img>
. - Trong lần render lại, React sẽ tính toán xem có thuộc tính nào của chúng đã thay đổi kể từ lần render trước đó không. React sẽ xử lý các thông tin đó ở tới bước tiếp theo, giai đoạn commit.
Deep Dive
Hành vi mặc định của việc render tất cả các component lồng bên trong một component được cập nhật sẽ không tối ưu cho hiệu suất nếu component đó nằm ở một vị trí cao trong cây. Nếu bạn gặp phải vấn đề về hiệu suất, có rất nhiều cách chủ động để giải quyết nó được mô tả cụ thể ở trong phần Performance. Nhưng đừng vội vàng tối ưu hóa!
Step 3: React commit các thay đổi vào DOM
Sau khi render (gọi tới) các component của bạn, React sẽ thay đổi DOM.
-
Đối với lần render khởi tạo, React sẽ dùng DOM API
appendChild()
để hiển thị tất cả các DOM node mà nó đã tạo ra lên trên màn hình. -
Đối với các lần render lại, React sẽ áp dụng các thao tác tối thiểu cần thiết (được tính toán trong khi render!) để làm cho DOM khớp với kết quả render mới nhất.
React chỉ thay đổi các DOM node nếu có sự khác biệt giữa các lần render. Ví dụ, dưới đây là một component bị render lại mỗi giây với các props khác nhau được truyền từ component cha của nó. Lưu ý rằng bạn có thể thêm văn bản vào <input>
, cập nhật value
của nó, nhưng đoạn văn bản kia sẽ không biến mất khi component render lại:
export default function Clock({ time }) { return ( <> <h1>{time}</h1> <input /> </> ); }
Điều này hoạt động bởi vì trong bước cuối cùng này, React chỉ cập nhật nội dung của thẻ <h1>
với giá trị time
mới. Nó thấy rằng thẻ <input>
vẫn xuất hiện tại vị trí đó trong đoạn JSX, nên React không hề đụng vào thẻ <input>
hay thuộc tính value
của nó!
Tổng kết: Trình duyệt vẽ
Sau khi quá trình render được hoàn tất và React đã cập nhật xong DOM, trình duyệt sẽ tiến hành vẽ lại màn hình. Mặc dù quá trình này thường được biết đến là “render”, nhưng chúng ta sẽ gọi nó là “vẽ” để tránh nhầm lẫn xuyên suốt tài liệu này.
Illustrated by Rachel Lee Nabors