react redux-saga where to handle some logic - reactjs

I am using redux-saga with React. Lets say i'm fetching some data from server that need some external client side calculation. My question is where to put them.
my saga looks something like this:
function* someEffect(action){
try{
//removed none related stuff from the code.
const data = yield call(someRequestFucntionThatUsesAxios, requestparams);
if(data.status>=200&&data.status<300){
yield put({type:SUCCESS, payload:data.data)};
}
catch(err){
}
}
In the above code data.data needs some calculation(basic math mostly). Now, which one of these options makes more sense?
handling those calculations in my connected components componentDidUpdate method.
handling them right after receiving it (before dispatching to to reducer).
(I know that reducer is no place for such behaviour but since it doesn't kill to ask,) handling it inside my reducer.

As you have said reducer is not the best place to modify the API response, neither saga is.
I would suggest you to use transformResponse function in axios, to modify the response just right after it is returned from API.
axios.get('/', {
transformResponse: (data) => {
// do whatever you like with the data
},
});

Related

Communication Between Component and API using Redux- Saga

How can we get the data from the saga directly into our component ?
or
Is this a pattern we should not follow and directly make a service/ api
call from the component using some service layer.
I have been following this issue on git on if this is possible there are too many permutations and combinations and i am a bit confused ..
I tried this small example by refering this
stackblitz.
In this case when i try and return this
function* helloSaga() {
return new Promise(function(resolve, reject) {
resolve('start of new Promise');
});
}
and access it like this
let response = dispatch(action('SHOW')).then(data => {
console.log(data); // i cannot get this to work say if this were a api response .
})
Nothing happens .
Is this pattern acceptable if yes then how can we make it work and what am i missing it
And if this pattern is an anti-pattern then making service calls from a layer like getData() should be enough from componentDidMount() .
A generator function doesn't really return a value (or the promise in your case) as you'd expect with any "regular" function. It returns a generator-object that you can pause, continue, cancel etc.
Check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
The way to go with redux-saga in a web app would be:
Trigger an action like "SHOW" in your example
Have a saga take your action "SHOW"
The saga would then do the async API request
Once the API request is done, the saga dispatches another action (something like FETCH_SUCCEEDED
Handle the action in your reducer and store the response in the redux store.
Once it's stored in the redux store, you can select it from any react component you want to (use connect from redux for that)
Here's your stackblitz with a minimal example:
https://stackblitz.com/edit/react-redux-sagas-demo-app-4tpevb?file=index.js
For a production app, you'd want to wrap the API request in a try{..}catch(){..} and handle the error with a proper action like FETCH_FAILED and display an error message in your FE (if it effects the user).

Await value returned from a redux-thunk dispatch rather than using state

I am using react, redux, and redux-thunk have a set of queries I have to run in the "logic layer". The results of each "action" call determine the path I take, so I am actually awaiting the promises returned from the dispatch call to the redux-thunks.
const handleClick = async (args) => {
let foo = await dispatch(fetchFoo(args));
if(foo) { do something... }
else {
let bar = await dispatch(fetchBar(args));
if(bar) { do another thing... }
else { or do another thing... }
}
};
Example thunk:
export const fetchFoo = args => async (dispatch) => {
let foo = await api.fetchFoo(args);
dispatch(fetchSuccess(foo));
// !!!!!!!!!!
return foo;
// !!!!!!!!!!
}
If I don't do this, it's pretty awkward to wait until a re-render (maybe) puts "foo" in the redux state prop, then wait again until a re-render (maybe) puts "bar" in the redux state, etc...
I've never really seen this pattern before although I have seen awaiting void promises return from thunks.
Is it acceptable to return the value from the redux-thunk action and use it rather than getting the values from a redux state selector? This seems to break the rules of the "single source of truth." If not, what do I do?
Using this approach it would be much harder to refactor state and change flow how data is loaded. Say in case requirements are changed and fetchBar() should be called by interval or need to be loaded by parent and passed through props. Or you want to throttle loading. Or cache data for some time.
I used this pattern with Promise returned and found it makes things rather complicated. The only advantage I found: I didn't need having isLoading or loadingError flags in redux storage to know if data is already loaded or not.
But also it means I have to call some methods redundantly just to retrieve brand new Promise await for - since promises can be resolved only once - in order to wait until some data loaded. And once some action triggers few API calls in sequence I ended with awaiting for data I did not even need.
In opposite if we rely only on data in redux it would be as easy as const isStillLoading = props.isData1Loading && props.isData2Loading
Consider moving condition for loading data into action itself. If you find API calls sequence becomes too complex you may switch to redux-saga or redux-loop which both provide better control over execution flow.

Get state and make dispatch from middleware

In my React application i'm using Long-polling API. In order to automatically send requests on every response i use middleware. But before sending new request, i have to save received data in store. More than that, i want to dispatch another action inside my middleware. So my structure looks like this:
InitLongPoll() -> SendRequest(data) -> ReceiveResponse(data)* -> SendRequest(data)
'*' is my middleware. From there i'm saving data to the store using store.dispatch(responseData) and sending new request using store.dispatch(sendRequest(authData)).
Is it okay to receive that authData using store.getState().authReducer? As far as i know, my middleware should be a pure function and shouldn't depend on external data (store). Thanks in advance.
Is it okay to receive that authData using
store.getState().authReducer? As far as i know, my middleware should
be a pure function and shouldn't depend on external data (store).
Yes it is. Middleware is the way to introduce side effects into the redux loop, and it can't be a pure function. Your own middleware has a side effect - polling the server.
A redux middleware is the anti-thesis of a pure function, which is defined as:
The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any
hidden information or state that may change while program execution
proceeds or between different executions of the program, nor can it
depend on any external input from I/O devices (usually—see below).
Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output
to I/O devices (usually—see below).
You can also see in the redux-thunk source code, that it uses getState:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}

Which is more preferable? Dispatch the response of a promise or dispatch an action stating a promise needs to be triggered?

Here are two code samples:
onClick() { // click-handler of a button
axios.get(someUrl)
.then(response => {
// setData is a fn dispatching an action-creator passed through react-redux's connect()
setData(response.data);
});
}
or
// buttonClicked is also a fn dispatching an action-creator
// Difference being the middle-ware handles the entire async process
<button onClick={this.buttonClicked}>Click me</button>
The latter method will use Axios in some middleware, and then dispatch another action which will set the response data in the store.
So this means that the first approach will only dispatch one action, while the second approach will dispatch two actions.
Both ways obviously seem to work, but I would like to know what the best way would be. Is there a downside to either approach?
Disclaimer: This is an opinionated answer, and somewhat rambly.
The thing about promises is that they work the way a human being would think of a promise. So use them like that in your program. Typically IMO you should only use Promises when you know that an event would occur in the normal course of your program workflow, or when you are promising a result.
So for example if you ask for a socket connection, I promise to give one to you whenever I am able to, you don't have to wait for me, just go on and do your thing, as soon as I have done everything needed to get that back to you I will hand it to you; and you can move on in your workflow from the point that needs it. For example, (pseudo code):
var Socket = new Promise(function(resolve, reject) {
resolve(do_something_to_get_a_socket());
});
Socket.then(authenticate()).then(sendData());
etc.
Sticking a promise to an event handler like onClick should be a promise to do something for the user — use it in your code to create threads that will do the heavy lifting of complex processing, while the user is still able to interact with the interface.
For example, in a game a click could fire a dart and you promise that it will animate on the screen (and even if that glitches) you still promise that it will hit the target etc, but the user doesn't have to wait for the promise to be fulfilled to fire another dart.
So use Promises to make your program more readable by you and other coders, and use it to make workflow of your program more realistic to your usecase.
I heavily recommend something like: https://www.npmjs.com/package/redux-api-middleware
This middleware (or others like it) contain quite a few features you would most likely have to write yourself if you were to implement this with just axios in a callback. For example, it will automatically dispatch request, success, and failure actions based on the AJAX call's result.
When dispatching this action using the middleware, many things are taken care of for you.
{
[CALL_API]: {
endpoint: "http://example-api.com/endpoint",
method: "GET",
headers: { ... },
types: [
"GET_X_REQUEST", "GET_X_SUCCESS", "GET_X_FAILURE"
]
}
}
Something like this will automatically fire a "GET_X_REQUEST" action when it begins to load. Then a success or failure action (with appropriate data or error objects attached as a payload) when the AJAX call completes or fails.
Or any similar middleware where Redux ends up handling the entire async process.

Why use redux-thunk or redux-saga for fetches?

I keep reading that I should use redux-thunk or redux-saga to handle side effects.
Why not simply use action creators like that to dispatch multiple actions :
function loadProductActionCreator(dispatch) {
dispatch({
type: 'load_product',
})
fetch('api/product').then(
function (r) {
return r.json();
}
)
.then(function (res) {
dispatch({
type: 'loaded_product',
data: res
})
})
}
I tried that and it worked (complete code). So I guess there must be some inconvenients I'm not aware of.
You code is similar to what thunk does.
As per redux docs, actions should be pure. And they should always return same values for same input parameters. By using fetch you are allowing action to return not specific value, rather value from server and that mean action response may vary upon time.
That is called side effects. And it's something what shouldn't be in redux actions by default.
But why?
Yes, you can type it inside action like you have, in small apps it does not matter.
In larger application there are benefits of using redux-saga:
actions are predictable, they just return payload like
{
type: 'FETCH_POSTS',
params: {
category: 'programming'
}
}
and then you build middleware which will take actions with all data required to perform request to real API
Possible advantages:
Cleaner codebase (but may be overhead on smaller applications)
Separation of "dummy" actions with all required information to perform requests and actual API middleware
Request parameters are visible directly in redux dev tools
Possible to easily debounce, throttle fetches which may be really tricky with redux-thunk
Possible to easily combine actions (wait for another event/fetch, chain events)
Possible to stop running tasks
From personal experience, on one project (larger codebase) we have started with redux-thunk, but later we needed to integrate more advanced features, like throttle, and some dependencies between actions. So we rewrote everything to redux-saga and it worked well for us.
You are kind of replicating redux-thunk here. A pure redux action creator should return an action object to be dispatched and not dispatch an action itself (see redux doc on action creator).
To better understand why your technic is a replication of redux-thunk, look at this post from its author

Resources