Where should the response logic happen, in the saga or the reducer?
Asked Answered
B

2

6

Let's say I have a saga that looks so:

export function* incrementAsync(action) {
  try {
    const res = yield call(Api.signin.create, action.payload);
    yield put({ 
      type: USER_SIGN_IN_FETCH_SUCCESS,
      payload: res.data.auth
    };
  } catch (e) {
    yield put({ type: USER_SIGN_IN_FETCH_ERROR_NETWORK });
  }
}

The fech was a success, but that doesn't mean that the user was actually logged in:

res.data.auth.error could be true

My question is whether I should do things like:

if (//user was succesfully logged in)
    yield put(//user was successfully logged in)
else if //wrong username
    yield put(//wrong username)
else if //wrong password
    yield put(//wrong password)

Or should I have only one for success and one for error, and in the reducer analyze the logic and build the store relative to the response data?

Biskra answered 8/3, 2019 at 20:17 Comment(11)
Reducers only job is to change the state according to an action. You should design meaningful actions that trigger predictable state changes.Gaseous
Ok, perfect, but that means all the logic goes in the saga generator function?Biskra
Yes, because the USER_SIGN_IN_FETCH_SUCCESS only makes sense if there's some kind of loading animation going on, since it's not necessarily a login success. The saga should yield meaningful actions, so your whole logic is clearer, and easier to debug through the Redux dev tools.Gaseous
This is about reducers vs action creators but is just as valid for sagas: redux.js.org/faq/…Summersault
Personally if there is extractable logic you need to do I prefer to put it into a utility file. Then you can import it in reducer or saga based on your needs and it won't affect your code much either way.Summersault
I wrote my comments as if it was black or white, but it's more a gray area, like in the link shared by Martin. Personally, I use redux-thunk-actions lib which deals with async state actions (STARTED, SUCCEEDED, FAILED, etc.) and the rest of the logic goes into my API integration code (mostly vanilla JS wrapping API calls returning promises).Gaseous
@MartinKadlec ok awesome! I think that pretty much explains it, I think it should make no difference that I'm on a saga function, That said if the answer is finding a balance that seems like a weird suggestion, cause I'd rather go with one or the other but keep consistent.Biskra
@EmileBergeron thanks!Biskra
@MartinKadlec yeah a util function there might make sense.Biskra
"rather go with one or the other but keep consistent", that's a huge part of programming that everyone will agree upon.Gaseous
I prefer use selector for data analyzeConglobate
L
3

Error logic should always be handled at sagas. In this particular case your API is not throwing a correct error because if your API call was not a success (200, for example), that logic should be handled at your catch statement.

Why is this error not being handled there? If you are using axios, this could happen as a consequence of a bad design of the API (i.e. returning 200 instead of 400 for an error in signin in). If it's just you doing it by hand you should throw an error and handle that logic at catch in sagas.

So my recommendation is:

  • Throw an error to the sagas to handle error logic at catch statement.
  • If you have to parse the response in order to programmatically throw the error do it at the API layer if you can.

  • Make a specific action to handle the signup error OR just make a generic FAIL action and pass an error message to it(an then store it at redux to show it).

It should look something like:

export function* incrementAsync(action) {
  try {
    const res = yield call(Api.signin.create, action.payload);
    yield put({ 
      type: USER_SIGN_IN_FETCH_SUCCESS,
      payload: res.data.auth
    };
  } catch (error) {
    yield put({ type: USER_SIGN_IN_FAIL, payload: error.message });
  }
}
Ledoux answered 9/3, 2019 at 2:42 Comment(0)
O
-1

I'd always move as much logic as possible to the reducer. Logic there is more visible in the dev tools, if you do it in the saga it can be harder. It is also easier to test, as it's a synchronous and pure function. Also, USER_SIGN_IN_FETCH_SUCCESS seems perfectly meaningful to me for an action send from saga to reducer (actions from components to reducer should be less technical).

Or answered 9/3, 2019 at 0:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.