dispatching API actions and loading stage - reactjs

We know that if a Redux action triggers an API call to a server (whether in Redux middleware or Redux Thunk), it takes time to receive the answer from the server. During this waiting phase, the UI must somehow shows the user that some loading is being done (showing an Spinner for example). In React and React native, a famous trick to handle these common situations is a isLoading boolean flag in the Redux state and of course, a loading action being dispatched. This boolean will be toggled once the answer is ready to be shown, so that I can update the UI.
However, after applying this trick for years, what I've got is an application full of bugs and errors and a super dirty code with a lot of redundant code.
I have checked all the React life cycle hooks to check the order, in which the hooks are called and the process of Redux dispatching. It seems that Redux and React works totally separately. (I know that getDerivedStateFromProps is called once the store has been updated, but it does not solve my problem)
I need a better way to handle these common situations. I don't know if I need to make modification in Redux part of my application, or in the UI, or ...

Related

Callback in Redux/Redux-Saga is an Anti-pattern?

Let's figure it out,
An user performs a login submission, so app shows instead a
Submit button a Spinner, a self contained state whose help us (isLoading).
Okay, when application send to saga login action we can pass a callback
for set false loading state when login submission has successful or failure.
Some experts will say, manage loading state in reducers, but carry to all whole application
loading state, for some specific action not sounds good.
The problem with callbacks is that the architecture doesn't guarantee that the callback gets called or that it won't get called multiple times. That is because redux actions are essential events - where each event can be handled by 0-n handlers (or sagas in our case).
Of course at the time of writing you know that that particular code is handled exactly once, but for anyone else this might be hard to grasp unless there are strict rules in the project how to handle this.
At the same time, you are right that putting local state to redux store isn't great. I usually deal with this by moving the data logic to its own structure. So e.g. loading collections of items from server is no longer local state of some component bur rather global data state that can be used and reused by multiple parts of the applications. This will also make it easier to have custom caching logic for the data cross whole application etc. However, some local component state in redux is still unavoidable for some specific backend calls.
In terms of future, I saw some attempts at useSaga hook, which would work on top of local useReducer hook and therefore local state, however the implementation for such logic is still limited because the current react hook api lacks certain functionality that is necessary to make sure this works well with react commit phase, render bail outs, reducer reruns etc.

Using react hooks in conjunction with redux-saga (or any other middleware)

This question was already asked here before, however I didn't find yet any proper answer, just wonder if I missed anything or if indeed this is currently not possible.
Specifically, there are 2 main differences between hooks & redux-saga (or any other middleware):
in redux-saga I can trigger code execution on action dispatch, while in hooks I can trigger code execution only on state change. These 2 are not always equivalent
hooks must exist in context of component, however some "background" logic (such as initialization, location detection, etc), is not related to a specific component. In redux-saga I have the flexibility to define it in "global scope", while in hooks I must attach it to some "random" component (probably App or any other high level component).
So, is it possible to "bridge" somehow between these 2 approaches, or should I simply pick the most appropriate technique for each specific case ?
They are different tools to solve different problems. Hooks work internally to a functional component's state and lifecycle. Redux works internally to an entire react app's state and lifecycle. Sagas help wrangle asynchronous effects like external data fetches.
Generally speaking you want to limit the scope of variables and logic as much as possible. If a specific piece of "state" is only relevant to a single component, then keep it in component state. But if several components or the application itself needs it, then store it in app state. Same applies for asynchronous calls. If a single external call is used by only one component, keep it there, but if multiple components can make the same external async calls, then let the sagas handle them.
You are free to use as much, or as little, as is necessary of either in each component to solve your problem.
In Redux-saga you can execute code only on action dispatch. Hooks are more generic, for example with the useReducer hook you can trigger code on action dispatch, with useEffect you can trigger code on mount/update/unmount etc.
Redux-saga also exist in context of a component, or more precisely, whole Redux exists in a (usually top-level) store provider component and Redux-saga is only one of its middlewares. So basically if you have some global app logic and put a hook for it in your App component, it is not some random component, it is the right place to put the logic to.
I've also recently found a simple library https://www.npmjs.com/package/use-saga-reducer that introduces sagas to React's useReducer. (I'm still not sure it is a good idea - just because you can doesn't mean you should, for me hooks async functions are usually enough, but it is at least interesting)

Redux devtools coding guidelines to play nicely with time travel debugging?

We are using react-connected-router, and in our react page components, we fetch state from the server, and dispatch actions to update the Redux state (we are just using hooks, no thunks nor saga middleware).
However, when we use the redux devtools for time travel debugging, the component will dispatch actions again when we rewind to the corresponding state...
I don't want to dispatch new actions when doing time travel debugging.
To be honest, I never want to do anything but rendering in my components, I typically do async fetches in event handlers (like click), but this approach doesn't work with React Router. Ideally when the browser history is changed by the user, some event should be fired, and async fetches should be done by handling that event, and not when rendering the page component, but I can't find documentation of how to do that.
My general question is, how does one write code that plays nicely with the devtools time travel debugger, so that new actions are not dispatched when just replaying state?
As far as I understand, middleware like Redux thunk or saga doesn't solve this problem either, unless the devtools would completely ignore Redux thunks being dispatched when doing time travel debugging. Maybe this is already the case, and I should just use these middlewares
It seems the Redux devtools have an option to lock new actions from being dispatched!
It is explained in this blog
So the guidelines seem to be to just fire these actions, and have the developer manually lock the changes.
I'm not 100% happy with this, it just feels wrong to dispatch actions as a side effect during component rendering, but so be it.

Best way to integrate React, Redux, Redux-Form, and React-Saga?

I have read everything I can find on how to use react-saga and redux-form together I'm stuck at a crossroads and need some advice.
Here is how I ended up here.
I chose to work with Redux because it makes sense for my app and I don't have any problems there.
Switch from Thunk to Sagas
I started working on my async api calls to populate my app with data. I'm using a lot of data grids and I'm not sure where I'll end up on latency as some of the queries are quite complex.
When I started working on the action creators and methods for doing background polling of data I realized that Sagas were going to be much easier than Thunk.
I made that switch and I'm happy with it. Handling any errors from the api calls is being handled by an action creator that updates redux state container for all of my api calls. Errors are displayed by populating a modal based on those state changes.
Introduciton of Redux-Form
Once I started working on posting form data back to the api things got interesting and I realized I was about to write a lot of code to handle it all through Redux. Redux-form simplified things as it has a state container for everything form related and makes it really easy to setup and validate forms on the client side.
Redux-form handles all of the form state in my configuration until the form is submitted.
I'm using a container component conected to a Redux store I created to hold state of api requests.
The form is a child component of the container that is connected to Redux through redux-form which handles all of it's state.
When the form is submitted I am calling a Redux action which in turn called a saga to post the data.
The result of the saga api call is to either dispatch a success or failure action in Redux. I am passing an object to the api state that contains the status (success/failure), an error object with any errors, and a return object where I can return things like the id's of records just created.
That works well when the error from the api is communication related. Because I'm updating the api state my higher level app components have access to it so I can do things like trigger a modal for errors not specifically related to the form data itself.
But when I started thinking of how I would handle any field errors that could occur if the client side validation was missing some logic I got lost.
In my current setup those errors would be on the api state object. I could put them into a modal but there would be no client side error handling on the form itself.
Redux-form can handle server side validation tied directly back to the form fields but only from a promise and from what I can tell trying to return a promise through action creators would be difficult if not impossible.
I can write a promise in my onSubmit function but I would have to call my saga function directly instead of triggering it through an action. Is that an acceptable pattern?
I guess I could trigger an action from the saga to populate my api state values but it seems backwards.
Basically I would prefer to handle comm errors one way (through my api state container) and form field data errors another way (back throu redux-form and it's error handlers) and I'm not sure which direction to take.
I looked at a module redux-form-saga which makes it possible to return a promise directly back to the form and therefore use the redux-form error handling after the api call but I'm not sure if I would be able to also trigger my api state actions at the same time.
Rather than continuing going down the rabbit hole and maybe over complicating things I thought I would solicit some advice from anyone who has had to deal with something similar.
I'm good with async background calls to populate my data grids but when I have to post data back to the api I want to make sure the user can't take any other actions until they get a response back.
This is the first section of many in this app so I want to create a design pattern that makes sense, is easily reproducible, is reliable, and easy to follow.
Any suggestions?
React-Boilerplate will help you integrate React, redux, redux-saga. On top of that integrating redux-form should be straightforward. React-Boilerplate uses all the current best practices of the community for a production ready app

Should I call async API's from a React Redux component? ( a new take on it)

I realize this question has been asked before and this topic has been widely discussed in the Redux community, but I have not seen it approached by this angle: Error messages.
In most examples using React + Redux + some middleware (redux-promise and redux-thunk), external api calls are done inside the action creator. The result of the API call then affects the application state with a success case or error case.
My counter-argument:
The main interested party in the results of an API call is a component, particularly because it's the one that has to often show an error message to the user. Error messages are best set as component state. It's easier to "clean up" on componentWillMount. No need to create an action just to clean up an application level error state.
All API call's should be made from a component and it should decide what action creator to call. Action creators then become JUST that, functions that return objects. No side-effects in them.
Again, I stress that this "take" is based on the fact that most of the time, a component will need to handle error messages anyways. So why not call the api and deal with the error right there? Things go ok, call an action creator. Things go bad, show an error. Also, I don't think there will be duplication of API calls across the application. After all, React tries to enforce modularization and top-down flow of data. Two different components really shouldn't be calling the same api. They could call the same action creator though and that's fine. Think sign up and sign in. Different api endpoints. Same final state (authenticated: true)
Anyway, this is my view on it. I'm hoping that someone with more experience will answer if API calls inside components are a good idea. Thank you.
EDIT: Just created this post on medium, which hopefully explains my argument better
Kind of too open ended to come up with a "solution" but here's a short answer.
First off, what do you mean it's easier to clean up on componentWillMount? Many times api calls are done on an already mounted component like a sign up or login component. The API call happens when the button is clicked, not when it's mounted.
Also, the main reason why API calls are done outside React components (assuming you have a data handling framework like redux) is that the library is used as a View layer. A component renders HTML that declaratively reflects the state of your application. When a login API call fails to authenticate, the application state is what changes, and as a result the View. If you start to handle API responses in your component, you may run into issues with out of sync state.
For example, the user logs in 10 times with the wrong credentials and gets "locked out". How do you handle that error? You'll likely add some logic to handle those errors. And what if other parts of the app need to react to this error? Now you start to fire actions based on those errors and essentially go back to making your API calls entirely from an action creator, which happens to live in your component.
Now, this mostly applies to large applications. It's perfectly reasonable to handle API calls in a component if the application is small enough and state management frameworks like redux just add bloat. If it's a large application, however, I still highly recommend keeping API logic in the action creators.

Resources