Khi nào thì nên call API qua saga trong react?

Tình hình là mình có tham gia một dự án trên công ty dùng React, Redux, Saga. Dự án bắt đầu được hơn 1 tháng thì mình có join vào và cấu trúc code base của dự án cũng đã dựng xong rồi. Khi mình làm thì mấy anh trong dự án bắt phải get data từ API (khi dùng saga thường được gọi là side effect) thông qua việc dispatch một action, saga sẽ lắng nghe acion đấy và call API, sau đó dự liệu được truyền vào reducers, reducer update state trong store.

Theo mình thì những thông tin như authentication, user info, settings,… thì nên lưu vào store và khi cần những thông tin đó từ API thì ta nên dùng saga. Còn như hiện tại thì mỗi lần call API đề phải tạo action, actionCreators, saga worker, saga generator function, reducers, partial state trong store. Như vậy sẽ rất mất công kể cả với những request đơn giản như delete item,… chỉ với 1 params.

Vậy mình xin hỏi mọi người những use case nào thì ta nên call API qua saga, khi nào thì nên call API trực tiếp?

1 Like

Theo mình là thế này

Nếu gọi API nhưng phải chờ api xong và trong lúc đó block hết mọi hành động, ko cần xử lý async thì có thể không cần dispatch action để Saga quản lý.
Còn nếu gọi API và phải xử lý async (như bấm nút download chẳng hạn) thì phải dispatch action để Saga handle api đó async cho bạn.

Tuy nhiên khi dùng redux thì mọi state đều được quả lý qua root state. Nên khuyên bạn chịu khó dispatch action dù nó một action đơn giản để nó quản lý state cho bạn. Như vậy tập trung quản lý hơn và không phải để code gọi API linh tinh khắp nơi ở những component. Cực ban đầu nhưng về lâu dài dễ quản lý và bảo trì code sẽ đỡ cực khổ.

7 Likes

Vấn đề mình đang gặp phải là ở chỗ này đó. Nhiều tác vụ rất đơn giản mà dùng saga thành ra rất phức tạp.

Như chức năng đăng ký, thành công thì show ra một cái popup success, lỗi thì show thông báo lỗi từ server. Mấy anh trong công ty bắt dùng saga. Mình phải lưu 3 biến signUpSuccess, signUpFail, responseError trong store để component biết được khi nào success/ error thì show ra thông báo. Hơn nữa trong component thì lại phải dispatch một action reset lại các biến kia trong store về giá trị mặc định. Trong khi đó call trực tiếp api và chờ response trả về là xong.

Vấn đề đối mặt với action blocking khi sử dụng saga mình có tìm trên mạng khắp các diễn đàn thì gần như không có cách nào tối ưu cả. (mấy ông anh trong làm rồi cũng không biết :laughing: )

Mình thấy làm như thế này là hơi lạm dụng.

Mình thấy với ứng dụng lớn thì hợp hơn. Còn với ứng dụng không lớn, chỉ <= 4 người làm thì không cần thiết mọi thứ đều làm như bạn nói. Với mình thì chỉ cần Đơn giản, Dễ hiểu (KISS), và không khó maintain là quá ổn rồi.

1 Like

merge một cách thủ công bởi noname00

2 Likes

Nếu ứng dụng nhỏ gọn, bạn có thể gạ mấy anh dùng redux thunk thử. :wink:
Viết action và dispatch như redux bình thường, ko lằng nhằng gì luôn

Cái này mình cũng rất hay gặp luôn. Mình thì hay dùng redux-thunk hơn do app cùa mình ko có quá bự để dùng saga. Tuy nhiên thôi, để thằng store quản lý hết cũng đỡ lằng nhằng hơn là setState ở từng component.
Vậy nên mình có viết ra một generator để sinh các action để đỡ tốn công phải viết và copy lại :">
Cũng khá đơn giản, bạn thử tham khảo cách này và viết lại cho saga xem :3

const actionGenerator = (actionName) =>
  (status) =>
  (payload) => (
    {
      action: `${actionName}_${status}`,
      payload,
    }
);

Khi xài thì mình chỉ cần

const XActionGen = actionGenerator('X');
const XActionHandling = XActionGen('HANDLING');
const XActionSuccess = XActionGen('SUCCESS');
const XActionFailed = XActionGen('FAILED');
const xActionClear = XActionGen('CLEAR');

const XAction = () => async (dispatch) => {
 dispatch(XActionHandling());
 const res = await fetchApi('https://daynhauhoc.com');
 const json = await res.json();
 if (res.ok) dispatch(XActionSuccess(json));
 else dispatch(XActionFailed({errorMessage: json.message}));
}

Mình thì không nghĩ vậy. Việc ứng dụng nhỏ nếu không chăm chút ngay từ đầu cũng rất khó mở rộng và bảo trì sau này. Ngoài ra nếu ứng dụng nhỏ thì có thể dùng những library nhỏ hơn như mình đề cập ở trên (redux-thunk) chứ không nên tùy tiện OvOb
(Không biết có hiểu sai ý bạn không)

7 Likes

Vấn đề quan trọng của project là bảo trì và từng người trong team phối hợp ăn ý với nhau. Team của bạn đã dùng Saga để fetch API thì cứ dùng Saga đi. Vì đó là quy định từ trước của team rồi.

Giả sử sau này, bạn làm theo ý bạn. Rồi có 1 bug xảy ra tại phần Sign Up, hay đổi url gọi sign up, hay đơn thuần là theo các error khác, mở rộng nhiều cách login, blabla… Lúc đó bạn qua công ty khác rồi. Đứa khác vô sửa code, nó theo đúng quy trình công ty, đây là api thì nó mở bên API, mò không ra, hỏi lead, lead hỏi lại, tìm trong đó đi, nó lại bảo không có anh ơi, kèm mặt mếu :sob:. Lead thấy là lạ, xem vào, api gọi sign up của tui đâu, đứa nào làm cái này vậy, kèm mặt hốt hoảng :scream: Lead gọi Product Manager, hỏi ai code phần này vậy, PM xem task history, thì thấy bạn :kissing:. Lead tức tốc gọi điện hỏi: trước em làm phần này ở đâu vậy? cứu bọn anh với? :cry: Hỏi ra té ra gọi thì biết async API trong React component, do em lười nên code vô 1 chỗ cho nhanh :grin:. Thế là bạn đã có 1 quả bóp team thành công. :joy:


Trả lời đàng hoàng, thì dù bạn sử dụng gì Redux, Saga, Thunk, MobX, bạn cũng nên nhớ, tất cả những thứ đó đều là tool để bạn build sản phẩm, cũng như không có tool nào phù hợp tất cả các use-case.

Việc thực hiện 1 side effect ngay trong React Component hay thông qua Saga đều được hết. Theo mình, mình sẽ viết trực tiếp vào React Component nếu side effect chỉ ảnh hưởng 1 component, không ảnh hưởng component cha hay các component con. Các side effect đó thường là UI như animation, check/unchecked 1 checkbox, check giữa 1 tập các radio buttons, validate thuộc tính value của text input. Các side effect này quá nhỏ để có thể đưa vào Redux như 1 state riêng.

Các side effect liên quan đến business logic, như authentication, mình sẽ chuyển state liên quan lên Redux hơn là viết vào React Component.

Về store thì khi học Redux thường chỉ có 1 global store, nhưng bạn có thể tạo bao nhiêu store cũng được. Mình thiên về tạo nhiều store, mỗi store độc lập với nhau. Một app sẽ gồm 1 global store, chỉ lưu khi không có chỗ nào hợp lý để đặt vào. User store lưu trữ đăng nhập, mình sẽ lưu thông tin về sign up, login, logout vào user store, đây cũng là câu trả lời của mình cho câu hỏi bạn đưa. Wiki store để lưu các bài blog kĩ thuật, cart store lưu giỏ hàng, schedule store lưu lịch trình công việc nội bộ,…

11 Likes

Đoạn code này khá hay, thanks bạn. Mình làm cũng chưa nghĩ tới đoạn code generator này viết cho đỡ vất.

Mình thấy dùng redux-thunk có vẻ hợp lý hơn. Code cũng dễ bảo trì

1 Like

Làm gì mà đến mức này đâu bạn. :joy:

Code 1 trang cùng lắm chỉ 2-3 component call API qua service là cùng,trace lỗi cũng đâu đến lỗi khó.

Phần này mình đồng ý với bạn. Không nên lạm dụng. Như thế sẽ tự làm khổ mình thôi

có thể hiểu cái này side effect là mỗi khi call api từ phía server thì sẽ xảy ra cái hành động này ko nhỉ .

1 Like

Xin lỗi vì đã bump topic này!

Anh có thể giải thích thêm task API nào block được mọi hành động không? Nghĩa là app bị đơ, không có animation phải không?

Tại thời điểm chủ topic đăng bài viết này (năm 2018), redux toolkit đã ra đời rồi và có hỗ trợ các tính năng giúp giảm thiểu code, tự đặt tên action type (chỉ cần khai báo prefix của action type). Actions, reducer gom chung vào slice. Sao không sử dụng redux toolkit luôn cho tiện nhỉ? :thinking: Do redux toolkit chưa phổ biến (mới ở version đầu tiên v0.3.0) hay nó có nhược điểm gì vậy anh?

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?