React Data Flow

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

Screenshot from 2015-05-21 21:24:49.png

Stateprops

Dữ liệu trong React tất cả đều được quy về 2 loại, đó là stateprops.

  • State là trạng thái của 1 component, nó có thể được khởi tạo bên trong component hoặc được truyền từ component cha. State có thể được thay đổi bên trong component.

Screenshot from 2015-05-21 21:32:36.png

  • Props là các thuộc tính của 1 component, được truyền từ component cha. Khác với state, props không thể bị thay đổi bởi component sử dụng nó.

Screenshot from 2015-05-21 21:32:59.png

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, stateprops 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.

Screenshot from 2015-05-21 21:39:50.png

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.

Screenshot from 2015-05-21 21:56:18.png

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.

component.jpg

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.

Screenshot from 2015-05-25 08:55:41.png

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à ProductTableTotalBar, 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 FilterableproductTableSearchBar 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.

Screenshot from 2015-05-25 09:04:07.png

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