redux-saga là gì

Lưu ý: Bài viết dành cho các bạn đã có hiểu biết cơ bản về React.

Cấu trúc bài viết sau đây gồm ba phần: Phần I là lý thuyết cơ bản, chức năng và cách dùng lần lượt của React, Redux và Redux-Saga. Ở phần II, tác giả sẽ sử dụng hình ảnh liên tưởng để người đọc hiểu hơn chức năng của từng loại. Với phần III, tác giả sẽ phân tích vì sao việc kết hợp cả ba loại thư viện này mới là lựa chọn tuyệt vời nhất cho một ứng dụng React.

PHẦN I: ĐỊNH NGHĨA CHỨC NĂNG

React [Còn được gọi là React.js hay ReactJS] là một thư viện Javascript do facebook tạo ra, được duy trì bởi Facebook và cộng đồng các nhà phát triển mã nguồn mở trên toàn thế giới.

React là một thư viện để xây dựng giao diện người dùng [User Interfaces UI], đồng nghĩa với việc xử lý những gì ở tầng view, phần tương tác trực tiếp với người dùng cuối.

Hiện tại ngoài việc phát triển Single-page application thì React còn khá mạnh trong mảng phát triển ứng dụng mobile bằng React Native.

Ở phạm vi bài viết này thì bạn cần phải lưu ý:

Một ứng React dựa trên nhiều component. Mỗi component sẽ có state và props. Nếu state của một component thay đổi thì nó sẽ được render lại. Còn props thì được dùng để truyền data từ component cha đến component con.

Redux là một thư viện javascript mã nguồn mở. Để quản lý trạng thái của ứng dụng, Redux được sử dụng phổ biến trong React, tuy nhiên Angular hay Vue vẫn có thể sử dụng được.

Như đã lưu ý ở trên, mỗi component có state và props, và props chỉ truyền từ cha sang con, nhưng trong thực tế một data không chỉ được dùng ở cha và con mà còn dùng ở những component khác nên Redux được sinh ra và hoạt động trên 3 nguyên tắc chính:

  • Chỉ có một nguồn tin cậy duy nhất[Single source of truth]:Statecủa toàn bộ ứng được chứa trong mộtobject treenằm trongStoreduy nhất.
  • Trạng thái chỉ được phép đọc[State is read-only]: Cách duy nhất để thay đổi State của ứng dụng là dùng một Action [là 1 object mô tả những gì xảy ra]
  • Thay đổi được thực hiện với pure functions[Changes are made with pure functions]: Với mỗi actions, bạn phải chỉ định cách store thay đổi bằng reducers.

Tóm lại, Redux tạo một states lớn[gọi là store]bao bọc toàn bộ và chứa toàn bộ dữ liệu trong ứng dụng, khi nào cần lấy dữ liệu, người dùng cần gửi một cái action để lấy ra và tạo ra một reducer để định nghĩa sự thay đổi trạng thái của dữ liệu. Và để thay đổi trạng thái dữ liệu thì cần dispatch action đó.

Redux-Saga là một thư viện redux middleware [Trong Redux, middleware là một lớp nằm giữa Reducers và Dispatch Action] giúp bạn quản lý nhữngside effecttrong redux. Redux-Saga sử dụng Generators [function*] của ES6 để xử lýbất đồng bộ một cáchđồng bộ.

Ví dụ: Khi thực hiện login thì đầu tiên, bạn sẽ dispatch một cái action, ví dụ loginRequest[], nhưng khi bạn fetch lên thì dữ liệu trả về lại là promise. Hơn thế nữa, sau quá trình fetch đó bạn phải thực hiện 2 action là là{ type: LOGIN_SUCCESS, response }nếu login thành công và hoặc{ type: types.LOGIN_ERROR, error }nếu thất bại. Bạn không thể vào viết một async functionsloginRequest[]mà mỗi khi gọi lại phải actionLOGIN_REQUEST, sau đó await fetch rồi tiếp tục action khi có dữ liệu trả về được.

Như ví dụ trên, ta chỉ cần cung cấp cho Saga một hàm chính, hàm này sẽ kiểm tra các action trước khi vào store. Nếu action được Saga quan tâm [do bạn định nghĩa] thì sẽ thực thi function. Điểm tuyệt vời của Saga là function này là generator function [nhờ vậy, bạn có thể xử lý bất đồng bộ một cách dễ dàng].

Vậy là chúng ta đã được điểm qua các kiến thức lý thuyết cơ bản rồi. Nếu chưa hiểu rõ phần này, mời các bạn đọc ví dụ liên tưởng ở phần 2.

PHẦN II: VÍ DỤ LIÊN TƯỞNG

Gác lại phần lý thuyết, chúng ta hãy cùng biến những kiến thức khô khan thành hình ảnh câu chuyện gắn với thực tế nhé!

Để dễ hình dung, tác giả sử dụng hình ảnh của một bưu điệnhoạt động với nhiều bước như nhận thư, vận chuyển thư, xử lý thư và lưu trữVậy thì React, Redux và Redux-Saga sẽ đóng vai trò gì trong quy trình đó?

Thử tưởng tượng bưu điện là một đơn vị có chức năng chính là nhận thư của khách hàng, sau đó xử lý đơn giản như đóng gói, phân loại rồi lại chuyển cho đơn vị khác xử lý tiếp. Sau khi đơn vị đó xử lý xong, lại chuyển đến một bưu điện khác để giao lại thư cho khách hàng.

Và React được coi như là một bưu điện như thế, chức năng chính của React là giao tiếp với người dùng, xử lý logic đơn giản, sau đó lại chuyển cho phần Backend xử lý, lưu trữ. Sau khi Backend xử lý xong sẽ trả lại React để đưa kết quả lại cho người dùng.

Nói về thư từ, một bức thư [data] trong bưu điện sẽ được rất nhiều bên liên quan sử dụng. Ví dụ như:

  • Bên đóng dấu sẽ lấy thư để đóng dấu.
  • Bên kế toán lại lấy thư để đối chiếu thu tiền.
  • .
  • Bên vận chuyển lấy thư để chuyển cho đơn vị khác xử lý.

Với cách làm việc ban đầu thì:

  • Khách hàng gửi thư[props]cho bên đóng dấu, bên đóng dấu sẽ lấy và gửi lại[callback props]cho khách hàng.
  • Sau đó khách hàng lại gửi[props]cho bên kế toán để trả tiền, sau đó bên kế toán sẽ gửi lại[callback props]cho khách hàng.
  • .
  • Cuối cùng khách hàng sẽ trao bức thư[props]đã có toàn bộ thông tin cần thiết của bưu điện cho bên vận chuyển.

Cách này vừa phức tạp, vừa khó quản lý bởi vì chúng ta không biết được khách hàng đã đưa thư cho ai, hay trạng thái hiện tại như thế nào

Cách nói trên là sử dụng React mà không có Redux, và nhược điểm đề cập ở trên cũng là một trong những điểm yếu trong cách quản lý của React.

Và cách giải quyết tốt hơn cho vấn đề xử lý thư ở trên đó là:

  • Tạo một kho để quản lý toàn bộ thư.
  • Mỗi khi khách hàng gửi thư sẽ bỏ thư vào đó.
  • Bên đóng dấu cần thì báo[action]với quản lý kho. Quản lý kho sẽ hỏi bên phụ trách[reducer]về việc đóng dấu thư.
  • Bên kế toán cần thư thì báo[action] quản lý, quản lý hỏi phụ trách[reducer]với thư đã thu tiền thì đóng dấu vào thế nào.
  • .
  • Cuối cùng bên vận chuyển sẽ báo[action] quản lý, sau đó lấy thư đã được hoàn tất quy trình chuyển qua đơn vị khác xử lý.

Một điểm đáng chú ý là sẽ có quản lý toàn bộ kho thư từ, khi bên nào cần lấy thư [thay đổi trạng thái] phải báo với người này hoạt động[action]cần thay đổi là gì, và người này sẽ hỏi bên phụ trách[reducer]về việc làm thế nào để thay đổi, đồng thời ghi lại nhật ký để biết được thư đó đã được thay đổi qua những trạng thái như thế nào[Tree of Reducers].

Thử tưởng tượng, nếu trong quá trình chuyển thư trong thành phố X, chính quyền muốn kiểm soát nội dung của từng bức thư, nếu có nội dung phản động hoặc sai trái thì sẽ phải gửi lại cho chính quyền, còn thư bình thường sẽ tiếp tục chuyển đến người nhận.

Vậy bạn phải cử Saga giám sát như thế nào trong quá trình chuyển thư:

  • Đầu tiên, bạn cho một người giám sát toàn bộ hoạt động của bưu điện[Redux saga watchers].
  • Chỉ cần có bất cứ bức thư nào gửi đến thành phố X, thì bạn sẽ cử người[function generator]đến và thực hiện các hoạt động kiểm tra nội dung. Nếu trong quá trình kiểm tra thấy thư có nội dung sai trái thì gửi[action]cho chính quyền. Ngược lại thì gửi[action]cho người nhận.

Tương tự vậy với Saga, trong quá trình action một API hay thực hiện một hoạt động bất đồng bộ nào đó Saga sẽ kiểm tra và xác nhận có điểm gì cần quản lý không? Nếu có, ngay lập tức Saga sẽ đưa ra cách xử lý phù hợp.

PHẦN III: TẠI SAO LỰA CHỌN REDUX VÀ REDUX-SAGA ĐỂ CẶP ĐÔI VỚI REACT?

Tại sao lại chọn Redux?

Hãy cùng nhìn lại cách mà bưu điện quản lý giữa cách 1 và cách 2, bạn sẽ thấy việc không sử dụng Redux sẽ gây ra rất nhiều khó khăn.

Ngoài Redux ra, còn một lựa chọn tương tự là Flux do chính Facebook tạo ra, nhưng hiện tại Facebook cũng khuyến khích mọi người nên sử dụng Redux. Lý do là gì?

Dễ maintain: Redux rất chặt chẽ, nghiêm ngặt trong việc tổ chức code, làm cho code nhất quán và dễ bảo trì hơn.

Dễ dàng khi làm với team: Như đã nói ở trên, vì tổ chức code chặc chẽ giúp cho việc làm nhóm trở nên dễ dàng hơn, không gặp tình trạng mỗi người làm một cách khác nhau.

Dễ debug hơn: Redux ghi lại toàn bộ thay đổi của state, nên bạn có thể dễ dàng phát hiện bug xảy ra ở vị trí nào.

Dễ test hơn: Vì Redux là tập hợp các function nhỏ và độc lập với nhau.

Cộng đồng Redux: Công đồng lớn mạnh và đang phát triển rất nhanh. Nhờ đó, bạn có thể dễ dàng tìm kiếm các tài liệu, ví dụ mẫu cũng như được hỗ trợ nhanh chóng từ các lập trình viên giàu kinh nghiệm.

Vậy sao lại chọn Redux-saga:

Chúng ta chọn Redux đơn giản vì nó là lựa chọn tuyệt vời nhất cho hiện tại nhưng ngoài Redux-saga thì còn có Redux-thunk hay Redux-observable cũng là những thư viện redux middleware hữu dụng khác.

Trước khi so sánh, bạn hãy đọc 2 đoạn code sau:

export const loginThunk =
[name: string, password: string] =>
[dispatch: Function] => {
dispatch[loginRequest[]];
try {
api.login[name, password];
}
catch [err] {
dispatch[loginError[err]];
return;
}
dispatch[loginSuccess[]];
};

Và đây là đoạn code tiếp theo:

function* loginSaga[] {
const action: LoginRequestAction = yield take[LOGIN_REQUEST];
const { name, password } = action.payload;
try {
yield call[api.login, name, password];
}
catch [err] {
yield put[loginError[err]];
return;
}
yield put[loginSuccess[]];
}

Nhìn vào hai đoạn code trên, có thể thấy rằng, code của Saga trong sáng hơn của Thunk rất nhiều!

Hơn nữa vì Saga sử dụng generator function nên mình sẽ không cần phải dùng promise để then, catch nhiều khi gặp bất đồng bộ. mà chỉ dùng keywordyield nổi tiếng.

Vậy tại sao sự kết hợp cả ba là lựa chọn tuyệt vời nhất cho một ứng dụng React?

Thứ nhất, để quán lý state trong React, nếu so với Flux, như đã phân tích ở trên thì Redux có ưu thế hơn là điều không thể phủ định.

Thứ hai, và trong các thư viện của Redux có 2 bộ thư viện được coi là mạnh nhất là Redux-thunk và Redux-saga, và với Redux-saga bạn có thể dùng generator function, giúp bạn có thể xử lý bất đồng bộ trong lúc gọi API một cách đồng bộ hơn. Nhờ dùng generator function mà bạn có thể cancel request, xử lý logic trong Saga một cách dễ dàng.

Các bạn có thể truy cập//www.npmtrends.com/redux-saga-vs-redux-thunk để xem xu hướng sử dụng Redux-saga và Redux-thunk hiện tại.

CO-WELL ASIA

Video liên quan

Chủ Đề