Redux cho người mới bắt đầu - Part 1 Introduction

redux.png

Hiện nay Reactjs là một thư viện mạnh mẽ khá phổ biến. Khi làm việc với React hay các dự án ứng dụng Single Page nói chung, có một vấn đề khá đau đầu là làm sao quản lý được trạng thái của ứng dụng đó. Sau khi xem qua giới thiệu về một số thư viện hỗ trợ công việc này, mình đã quyết định về với đội của Redux.

Trong loạt bài viết Redux cho người mới bắt đầu mình sẽ cùng các bạn tìm hiểu những khái niệm cơ bản và demo một ứng dụng nhỏ với Redux.

Trong phạm vi bài này mình sẽ giới thiệu về Redux còn code thì xin phép để bài sau (yaoming)

Chuẩn bị

Trước khi bắt đầu có một số khái niệm nên tìm hiểu qua:

Redux đã ra đời như thế nào ?

Năm Quý Tị (2013), Facebook gia tộc bố cáo thiên hạ rằng Ăn Gô La đại pháp (Angular) của Google gia tộc chậm chạp, nặng nề, cho xuất thế một bộ chiêu thức gọi là Rối An Tâm Pháp (React).

Thế nhưng Rối An Tâm Pháp lại chỉ là một bộ tâm pháp cường thân kiện thể, không thể dùng để rèn luyện nội công (chỉ là một library để render view). Do đó, không lâu sau Facebook gia tộc tiếp tục cho ra đời một bộ tâm pháp cơ bản (kiến trúc thiết kế) và một công pháp cùng tên là Phờ Lắc thần công (Flux). Nghe đồn Rối An Tâm Pháp và Phờ Lắc Thần Công kết hợp lại sẽ thành tuyệt học dời non lấp bể, không gì không làm đc. Nhân sĩ giang hồ (coder) vốn nhẹ dạ cả tin lại rủ nhau tu luyện.

Phờ Lắc thần công rối rắm khó học, nhân sĩ 10 phần học thì 4-5 phần tẩu hỏa nhập ma, phần còn lại cũng trầy da tróc vẩy mà công lực cũng chẳng được như lời Facebook gia tộc quảng cáo.

Bấy giờ có một nhân sĩ giang hồ tự là Đan (Dan Abramov), đang tu luyện đồng thời Phờ lắc thần công và Ê La thần công (Elm) mới nhận ra rằng hai môn võ công có nhiều điểm chung, chỉ khác chiêu thức, Đan bèn nảy ra ý định hợp nhất hai môn này lại. Không lâu sau (5/2015), Đan cho xuất thế một bộ công pháp mang tên Rì Đắt thần công (Redux), mang ưu điểm của cả hai môn võ công đồng thời loại bỏ những phức tạp dư thừa của Phờ Lắc thần công.

Nhân sĩ giang hồ nghe vậy mừng lắm, thế là lại kéo nhau đi học Rì Đắt, còn Đan thì được Facebook gia tộc mời về làm tộc nhân.

Trích "JavaScript Lược Sử Giang Hồ" có sửa đổi bổ sung (yaoming)

Redux là gì ?

Redux js là một thư viện Javascript giúp tạo ra thành một lớp quản lý trạng thái của ứng dụng.

Redux được xây dựng dựa trên nền tảng tư tưởng của ngôn ngữ Elm và kiến trúc Flux do Facebook giới thiệu.

Do vậy Redux thường là bộ đôi kết hợp hoàn hảo với React. Tuy nhiên hoàn toàn có thể sử dụng với các framework khác như Angular, Angular2, Backbone, Falcor, Deku.

Note: Reduxjs là một thư viện Javascript khác với Redux Framework là một WordPress Framework.

Nguyên lí

Redux được xây dựng dựa trên 3 nguyên lý:

  • Nguồn dữ liệu tin cậy duy nhất: State của toàn bộ ứng được chứa trong một object tree nằm trong Store duy nhất
  • Trạng thái chỉ được phép đọc: Cách duy nhất để thay đổi State của ứng dụng là phát một Action (là 1 object mô tả những gì xảy ra)
  • Thay đổi chỉ bằng hàm thuần túy: Để chỉ ra cách mà State được biến đổi bởi Action chúng ta dùng các pure function gọi là Reducer

Cấu trúc

02-redux-data-flow-opt-preview.png

Về cơ bản Redux có 4 thành phần như sau:

  • Action: Là nơi mang các thông tin dùng để gửi từ ứng dụng đến Store. Các thông tin này là 1 object mô tả những gì đã xảy ra.
export const INCREASE = 'INCREASE'
export function increase() {
        return {
            type: INCREASE
        }
}
  • Reducer: Là nơi xác định State thay đổi như thế nào.
export default function counterApp (state = initialState, action) {
        switch (action.type) {
            case INCREASE:
                return {
                    increase: ++state.increase,
                    decrease: state.decrease
                }
            case DECREASE:
                return {
                    increase: state.increase,
                    decrease: ++state.decrease
                }
            default:
                return state
        }
}
  • Store: Là nơi quản lý State, cho phép truy cập State qua getState(), update State qua dispatch(action), đăng kí listener qua subscribe(listener).
import { createStore } from 'redux'
import counterApp from './reducers'
let store = createStore(counterApp)
  • View: Hiển thị dữ liệu được cung cấp bởi Store

Data flow

Trong một ứng dụng thực tế, chúng ta có nhiều thành phần hơn, và chúng ta sẽ khó mà hiểu hết được các hoạt động của Redux nếu chỉ nhìn vào 4 thành phần cơ bản trên. Chúng ta sẽ tìm hiểu kĩ hơn thông qua các hình tượng minh họa sau.

Các nhân vật

Tưởng tượng ứng dụng của chúng ta là một văn phòng, có các thành viên làm việc với nhau để giải quyết công việc chung. Cùng điểm qua các nhân vật trong phòng trước khi xem các họ tương tác với nhau để giải quyết công việc:

The Action creators

action-creators.png

Đầu tiên là The Action Creators. Anh ta giữ nhiệm vụ tạo ra các Action, là bước đầu tiên trong luồng mà các thay đổi và tương tác đều đi qua. Bất cứ khi nào trạng thái của app hay là render của view thay đổi thì đầu tiên là một hành động sẽ được tạo ra.

Hãy hình dung anh này như một anh chàng đánh máy, anh ta biết bạn cần truyền đạt điều gì và cần phải đánh ra văn bản theo định dạng nào cho mọi người đều hiểu được.

The Action Creator tạo ra một action là formated object chứa type và thông tin của action đó. Type thường sẽ là một hằng số được định nghĩa trước, kiểu như INCREASE hay DECREASE.

The Store

The-Store.png

Tiếp theo là The Store. Hãy hình dung đây là ông sếp đầy quyền lực, toàn bộ các thao tác với State tree (getState, updateState, registerListener...) đều do ông này quản lý.

Cái ông này làm nhiều việc nhỉ (?) Không thực ra ông ấy chỉ quản lý trạng thái của State tree thôi. Khi nhận được Action ông ấy sẽ đi hỏi The reducers xem State sẽ thay đổi ra sao chứ không tự làm.

Nếu đã biết qua Flux thì hẳn là bạn nhận ra ông này nhận trực tiếp Action mà không thông qua Dispatcher. Đó là bởi vì trong Redux, nhờ tiếp nhận tư tưởng của Functional Programming, ông sếp này tự biết cách hiểu Action và tự điều phối nó (dispatch()), nên không cần thuê thêm anh Dispatcher.

The Reducers

The-Reducers.png

Tiếp theo là The Reducers. Khi The Store muốn biết State thay đổi như thế nào, ông ấy sẽ gọi cho The Reducers. Ở đây có một ông là Root Reducer nữa, ông này sẽ chịu trách nhiệm cắt ra State cần thay đổi dựa trên keys mà The Store gửi cho và đưa nó cho Reducer biết cách xử lý.

Hãy hình dung đây là một nhóm các thanh niên cuồng Photocopy (yaoming). Họ không thích làm rối tung những thứ họ được đưa cho, nên họ tạo ra bản sao của chúng và thực hiện thay đổi trên bản sao đó.

Đây là một trong những ý tưởng quan trọng của Redux. State không được thao tác trực tiếp. Thay vào đó, mỗi phần được sao chép và sau đó tất cả các phần được kết hợp thành một đối tượng trạng thái mới.

Các reducer gửi bản sao của chúng cho root reducer, và root reducer sẽ ghép các bản sao với nhau để tạo State mới. Sau đó, root reducer sẽ gửi các State mới trở lại Store và Store sẽ sử dụng nó như State chính thức mới.

Nếu ta có một ứng dụng nhỏ, ta chỉ có thể có một reducer làm cho một bản sao của toàn bộ State và và thay đổi nó. Hoặc nếu ta có một ứng dụng lớn, ta có thể có một Cây Reducers (Tree of Reducers) . Đây là một sự khác biệt giữa Flux và Redux. Trong Flux, các Store không nhất thiết phải kết nối với nhau và có một cấu trúc phẳng. Trong Redux, Reducers là một hệ thống cấp bậc, hệ thống cấp bậc này có thể có nhiều mức độ cần thiết, giống như các hệ thống cấp bậc component.

The views: smart and dumb components

The views.png

Trong Redux có 2 khái khái niệm: smart and dumb components.

  • Smart component: có thể gọi là containers
    • Hãy hình dung đây là anh quản lý của nhóm nhỏ, anh ta phụ trách các Action. Khi các thành viên dưới anh ta (dumb components) cần phát 1 action, anh ta sẽ gửi action cho các thành viên dưới dạng props, các thành viên chỉ cần coi đó là các callback mà không quan tâm nó là cái gì.
    • Anh ta không thích ăn diện (không có css).
    • Khi có việc cần thay đổi (DOM) thì anh ta sẽ sắp xếp các thành viên dưới làm chứ hiếm khi tự làm.
  • Dumb components: có thể gọi là components
    • Hãy hình dung đây là mấy thanh niên học việc, thiên lôi chỉ đâu đánh đấy.
    • Mấy thanh niên này không phụ thuộc trực tiếp vào các Action, vì được anh quản lý đưa cho rồi. Điều này có nghĩa là mấy thanh niên này có thể đưa sang bộ phận khác làm cũng đc, miễn là có anh quản lý đưa "hàng" cho xài.
    • Mấy thanh niên này thì đẹp trai, tóc tai vuốt vuốt các thứ (có css riêng), nhưng đôi khi bị cấp trên bắt mặc theo ý sếp (nhận props style) thì vẫn phải chịu.

The view layer binding

The view layer binding.png

Để The Store giao tiếp đc với The views, chúng ta cần một ai đó kết nối họ lại với nhau, và chúng ta có The view layer binding, với React anh ta tên là react-redux.

Hãy hình dung anh này là nhân viên IT chịu trách nhiệm đảm bảo mạng trong công ty thông suốt để các components kết nối được với Store. Đồng thời cũng quản lý một đống thông tin kỹ thuật mà trong văn phòng chả ai hiểu (yaoming)

The view layer binding cung cấp 3 khái niệm:

  • The Provider component: là thành phần bao quanh components tree. Giúp các components con kết nối với Store dễ dàng thông qua connect()
  • connect() : là function cung cấp bởi The view layer binding như react-redux. Nếu một component muốn nhận được update State, nó phải tự bao lại bằng connect(). Sau đó, connect function sẽ thiết lập tất cả các hệ thống liên kết cho nó, bằng cách sử dụng selector
  • selector : Đây là function mà bạn viết. Nó chỉ rõ phần nào của State mà component cần như properties.

The root component

The root component.png

Cuối cùng là The root component. Tất cả các React app đều có The root component. Là những component cao nhất của hệ thống component. Ở Redux thì nó đảm nhận nhiều trách nhiệm hơn.

Hãy hình dung đây là một giám đốc C-level (CEO, COO....). Ông giám đốc này sẽ tạo ra The Store và chỉ định The Reducers nào được sử dụng, tập hợp The view layer binding cùng với The views.

Sau khi chỉ định và tập hợp các thành phần trong team, giám đốc của chúng ta sẽ để cho các bộ phận bên dưới tự hoạt động.

Phối hợp giữa các nhân vật kể trên

Qua hết các nhân vật rồi, giờ cùng xem họ tương tác với nhau như thế nào để giải quyết công việc

Setup

Các bộ phận cần được nối với nhau. Việc này xảy ra lần đầu vào app.

1. Bảo ông Store sẵn sàng: ông giám đốc Root component tạo ra Store, chỉ cho ông Store dùng Root Reducer nào thông qua createStore(). Ông Root Reducer thì đã có team sẵn rồi, được tập hợp lại thông qua combineReducers()

Get the store ready.png

2. Chuẩn bị liên lạc giữa ông Store và các bộ phận khác.

Root component bao các subcomponents với provider component (The view layer binding) và tạo kết nối giữa Store với các Provider.

Provider tạo ra 1 mạng cơ bản để cập nhật các components. Smart Components kết nối vào mạng bằng connect(), điều này đảm bảo họ nhận được cập nhật State.

Set up the communication between the store and the components.png

3. Chuẩn bị các actions callback

Để các Dump Components làm việc với Action dễ dàng hơn, các Smart Components có thể chuẩn bị các action callback thông qua bindActionCreators(). Bằng cách này, họ chỉ có cần truyền các callback cho Dump Components. Các Actions sẽ được tự động gửi đi sau khi nó được định dạng.

Prepare the action callbacks.png

Flow

Sau phần setup thì web app đã sẵn sàng nhận input từ người dùng. Khi này trigger một action bằng cách để người dùng tạo ra một sự thay đổi.

trigger action.png

  1. The View yêu cầu 1 action. Action Creator định dạng (format) yêu cầu và gửi lại

redux-flow1.png

  1. Action được gửi tự động (nếu bindActionCreators() đã được chuẩn bị) hoặc The View sẽ gửi

redux-flow2.png

  1. The Store nhận Action sau đó gửi State tree hiện tại và Action cho Root Reducer.

redux-flow3.png

  1. Root Reducer chia State ra thành nhiều phần và gửi cho từng subreducers biết cách xử lý chúng.

redux-flow4.png

  1. Subreducers tạo ra 1 bản copy từ phần nhận được và thay đổi trên bản copy. Sau đó gửi lại bản copy cho Root Reducer

redux-flow5.png

  1. Khi tất cả subreducers trả về các phần copies, Root Reducer ghép chúng lại tạo thành 1 update State tree và gửi lại cho Store. Store thay thế State tree cũ bằng State tree mới.

redux-flow6.png

  1. Store nói với The view layer binding là có State mới.

redux-flow7.png

  1. The view layer binding báo Store gửi State mới cho mình

redux-flow8.png

  1. The view layer binding kích hoạt render view

redux-flow9.png

Kết

Vậy là chúng ta đã tìm hiểu được những khái niệm cơ bản đầu tiên của Redux như nguyên lý, cấu trúc, và data flow.

Thông qua phần 1 này hi vọng mọi người hình dung được các thành phần trong 1 app sử dụng Redux. Để hiểu kĩ hơn về cách sử dụng Redux, hẹn mọi người ở phần sau Project đầu tiên. (Bài này mang tính chất chém gió và thả thính là chính (yaoming))

Và hãy nhớ những nhân vật của chúng ta, để còn gặp trực tiếp họ trong phần sau.

Tài liệu tham khảo