React Data Flow
Bài đăng này đã không được cập nhật trong 3 năm
Cơ bản về React
Ở bài viết trước về React, Introduction to ReactJS, tôi đã giới thiệu 2 đặc điểm chính của React, đó là virtual DOM
và one-way data binding
. Nếu như virtual DOM
cung cấp 1 cách tiếp cận hoàn toàn mới để làm việc với HTML DOM
thì one-way data binding
lại là 1 phương thức quản lý luồng dữ liệu rất dễ hiểu và dễ quản lý. Trong bài viết này, tôi sẽ tập trung phân tích luồng dữ liệu trong React, hay nói cách khác là phân tích one-way data binding
.
One-way data binding
Data binding
là gì?
Trước tiên chúng ta cùng tìm hiểu về thuật ngữ data binding
. Data binding
là quá trình thiết lập kết nối giữa giao diện ứng dụng (application UI) và các chức năng ở tầng logic của ứng dụng (business logic). Sự kết nối này đảm bảo rằng mỗi thay đổi bên phía giao diện (application UI) sẽ tạo ra thay đổi đối với các dữ liệu, dựa theo logic của hệ thống (business logic). Khái niệm này thường được nói đến khi làm việc với các dịch vụ web (WPS - Web Processing Service). 1 ví dụ đơn giản là nếu chúng ta thay đổi nội dung nhập vào cho 1 khung tìm kiếm, các kết quả trả về sẽ được thay đổi dựa theo giá trị mà chúng ta nhập vào.
Cách tiếp cận data binding
thông thường
Với người lập trình web, dù biết hay không biết đến khái niệm data binding
, miễn là đã từng làm việc với javascript thì chắc chắn đã làm những công việc như được nêu ra trong định nghĩa ở trên. Vậy cách tiếp cận của họ là gì?
Thông thường, khi có sự thay đổi ở bất cứ thành phần nào của trang web, các thành phần khác của trang web sẽ được cập nhật thay đổi 1 cách tự động. Tuy nhiên, từ góc nhìn của người lập trình, sẽ không thấy được mối quan hệ thứ bậc giữa các thành phần đó. Và khi 1 thành phần bị thay đổi, thường rất khó để biết tại sao và khi nào nó thực sự bị thay đổi, các thành phần nào tác động trực tiếp đến sự thay đổi đó. Nói cách khác, trong lập trình giao diện web thông thường, bất cứ thành phần nào cũng có thể tương tác đến các thành phần khác mà không có 1 tổ chức quản lý rõ ràng.
Ưu điểm của cách tiếp cận này là giúp cho lập trình viên dễ dàng hơn khi xây dựng giao diện, bởi khi cần cập nhật chức năng, họ chỉ cần thêm vào các hàm quản lý là xong. Họ không phải suy nghĩ về tổ chức các thành phần trên trang web ngay từ đầu. Bởi chúng vốn dĩ không cần có tổ chức rõ ràng.
Tuy nhiên, nhược điểm của cách làm này đến từ sự thiếu tổ chức của nó. Các lập trình viên có thể thêm bớt các chức năng thoải mái mà không cần có tổ chức cụ thể. Điều này gây ra khó khăn khi hệ thống lớn lên, khó quản lý được luồng dữ liệu cũng như các thành phần tác động lên 1 đối tượng trên website. Khi trang web lớn lên thì việc kiểm tra lỗi và sửa lỗi trở nên rất khó khăn.
Đó là lý do tại sao one-way data binding
ra đời.
Data binding
trong React
Để đến được với khái niệm one-way data binding
trong React, trước tiên chúng ta cùng tìm hiểu các khái niệm cơ bản của React.
Component
Tất cả các thành phần trong 1 trang web đều được hiểu là component
. Với sự trợ giúp của virtual DOM
, các component
đều được khởi tạo dưới dạng Javascript Object và được truyển đồi thành HTML DOM
mỗi khi được render.
Sau đây là 1 ví dụ về component ứng với 1 input.
var InputName = React.createClass({
render: function() {
return (
<div>
Name: <input type="text" name="name" />
</div>
);
}
});
Và đây là những gì chúng ta nhìn thấy trên trang web.
State
và props
Dữ liệu trong React tất cả đều được quy về 2 loại, đó là state
và props
.
State
là trạng thái của 1component
, nó có thể được khởi tạo bên trongcomponent
hoặc được truyền từcomponent
cha. State có thể được thay đổi bên trongcomponent
.
Props
là các thuộc tính của 1component
, được truyền từcomponent
cha. Khác vớistate
,props
không thể bị thay đổi bởi component sử dụng nó.
Luồng dữ liệu trong React
Qua giới thiệu ở trên, chúng ta đã biết đến khái niệm component
, state
và props
trong React. Vậy luồng dữ liệu trong React như thế nào, one-way data binding
là gì?
Rất đơn giản và rõ ràng, dữ liệu trong React sẽ chỉ được truyền theo 1 chiều duy nhất, đó là từ component
cha đến component
con thông qua props
. Không có chiều ngược lại (thực ra là bạn có thể làm ngược lại nhưng như vậy là trái với quan điểm của React).
Câu hỏi đặt ra, liệu dữ liệu có thực sự được truyền theo 1 chiều duy nhất? Thường là không! Nhưng trong React, chúng ta coi việc truyền "thông tin" từ component
con đến component
cha là truyền "sự kiện". Trong sự kiện đó component
con có thể đính kèm các thông tin của sự kiện (có thể là dữ liệu). Quá trình đó được hiểu là truyền sự kiện, không phải truyền dữ liệu.
Tóm lại, trong React, dữ liệu sẽ được truyền từ trên xuống, và sự kiện được truyền từ dưới lên.
Ví dụ
Để có thể hiểu được luồng dữ liệu trong React, tôi sẽ phân tích 1 ví dụ trên tài liệu chính thức của React, qua đó giúp bạn đọc hiểu được rõ hơn tại sao one-way data binding
lại tuyệt vời đến như vậy.
Ví dụ được đưa ra dựa theo hướng dẫn Thinking in React với 1 chút chỉnh sửa cho phù hợp với nội dung bài viết này.
Bài toán
Chúng ta cần xây dựng 1 trang web tìm kiếm sản phẩm (product). Người dùng có thể tìm kiếm dựa theo tên sản phẩm, hoặc trạng thái của sản phẩm. Số lượng sản phẩm cũng như chi tiết từng sản phẩm sẽ được hiển thị mỗi khi người dùng thay đổi thông tin tìm kiếm.
Tổ chức component
Trong React, tất cả đều là component
, vậy nên, trước tiên chúng ta cùng chia nhỏ các component
trên trang web ra.
Theo như sơ đồ trên, chúng ta đã chia trang web thành 6 component
. Dưới đây sẽ là mô hình thứ bậc biểu diễn quan hệ cha-con giữa các component
.
Luồng dữ liệu
Phần tiếp theo là quyết định luồng dữ liệu, đây là phần quan trọng nhất vì nó quyết định đến cách hoạt động của trang web.
Chúng ta sẽ chỉ có 1 state
duy nhất, đó là danh sách các sản phẩm, thuộc component
lớn nhất, là FilterableProductTable
.
var FilterableProductTable = React.createClass({
getInitialState: function() {
return {
filteredProducts: this.props.products
};
},
...
});
2 component
con thực hiện hiển thị dữ liệu là ProductTable
và TotalBar
, sẽ nhận dữ liệu từ component
cha và thực hiện việc hiển thị dữ liệu đó. Trong khi ProductTable
sẽ nhận toàn bộ danh sách sản phẩm thì TotalBar
sẽ chỉ nhận tổng số sản phẩm.
var FilterableProductTable = React.createClass({
...
render: function() {
return (
<div className="filterable-product-table">
<TotalBar total={this.state.filteredProducts.length} />
<ProductTable products={this.state.filteredProducts} />
</div>
);
}
});
Component
con còn lại của FilterableproductTable
là SearchBar
sẽ đảm nhiệm việc truyền sự kiện lên cho component cha, khi có bất cứ thay đổi gì mà người dùng tạo ra trên form tìm kiếm.
var FilterableProductTable = React.createClass({
handleUserInput: function(filterText, inStockOnly) {
// Perform filter here
},
render: function() {
return (
<div className="filterable-product-table">
<SearchBar onUserInput={this.handleUserInput} />
...
</div>
);
}
});
Như vậy chúng ta đã quyết định xong luồng dữ liệu và luồng sự kiện cho trang web của mình. Có thể thấy đơn giản qua mô hình sau.
Trong mô hình trên, phần màu vàng là luồng dữ liệu và màu xanh là luồng sự kiện. Rõ ràng chúng ta có thể thấy, dữ liệu được truyền theo 1 chiều duy nhất!
Kết Luận
Để có thể hiểu được luồng dữ liệu trong React, tôi rất kỳ vọng bạn đọc có thể tham khảo bài viết sau của Facebook Thiking in React. Bài viết cho chúng ta hiểu được cách nghĩ cũng như cách làm việc với website theo kiểu React, có thể nói đó là 1 bước ngoặt lớn trong phát triển web.
Source code cho ví dụ được nêu ra trong bài viết có thể tham khảo ở đường link sau. https://github.com/dinhhoanglong91/reactjs-dataflow
All rights reserved