When we needed to define a stack for this project, due to its nature of involving a lot of visual presentation and 3D modeling, React presented itself as a no brainer choice, regarding the front-end technology.
Unfortunately, at that time, a clear strategy for state management wasn’t defined. As the project evolved, it started becoming an issue, so the team decided to migrate all the logic related to state management to a Class that presented itself as a Singleton. That would help with all the state-related storage and events. This solution was fine on the short-term, but eventually, it would outgrow his usefulness and a plan was set in motion to find a better alternative. It came in the form of Redux, helped by the introduction of Redux Toolkit, previously known as Redux Starter Kit.
Redux is a state container for JavaScript applications that lets you circumvent the natural React unidirectional data flow. This is possible as it has a source of truth that you can consult in your entire application, without having to drill downstate as a prop to other components. However, it also allows you to alter using predefined actions, while also keeping a history of such actions and mutations, that can be consulted while testing.
When Facebook presented React to the world, it was already evident for them how props could become a major hurdle, when reaching a certain level of complexity. To mitigate this issue, they introduced a concept alongside React called Flux, which describes how a store should work in conjunction with React. Redux, as we know today, stems from just a proof of concept hacked by Dan Abramov, while tinkering with Flux principles for React Europe.
Redux is used in large scale applications where an interaction with one component might propagate changes to the entire page. To facilitate this kind of communication between components, instead of having to create callbacks at the top level of the application, you can just consult the store. In my current project, Redux makes sense, because the application had escalated to a point where props related to its state were being passed down through several layers of components, which in turn was making the code hard to read and even harder to debug.
Redux looked like a godsend when we started migrating old code to this new feature. It made the code look slicker, it was extremely intuitive to understand and reduced drastically the number of props floating around the application. But it was not perfect. The very nature of reducers poses a problem when you’re encompassing the fetching of information in them, and that’s something the Redux community has been tackling for a very long time.
Reducers, in theory, are pure functions, according to the documentation itself.
Given the same arguments, it should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.
So, where should you apply async calls in Redux?
The Actions should be the immediate answer, but the basic implementation of actions is nothing more than a plain JavaScript object you use to pass information to your store. For this reason, the community came up with several middlewares that wrap all the logic into functions instead, which mimic the natural behavior of the store.
As with everything in programming, there isn't a one size fits all solution. So you should definitely research which middleware fits your problem the best. The first solution suggested by the documentation is Redux Thunk. This middleware allows you to create Actions as more than plain objects. These new Actions can dispatch other Actions, other Thunks and also perform async operations inside them. But recently, other middlewares have started gaining traction, like Redux-Saga and Redux-Observable have different use cases but they all share one thing, which is a very active repository and thriving community behind them.
Out of all the popular solutions to this issue, Redux Thunk is the easiest one to understand. It’s also fairly accessible in technical terms and, at this time, it’s the suggested way to approach this situation according to the Redux documentation.
We start here:
This will get you a fresh new app using React with all the Redux modules we needed to do this short tutorial. We will also be working with the WoofBot API service.
The first thing to do is to set up a dog slice, where you’ll keep all the information related to your dog API response.
We have our Actions:
and Selectors:
How would you normally implement this back and forth with the API and the store? I would fit it all into a useEffect hook, similar to this one:
What are we doing here?
Alternatively, we might receive an error from the API which will stop the flow in step 4 and set the Loading State to Error.
This works, and that’s ok. But it also has several downsides. Mainly, it puts too much logic in the component. It’s not reusable, and if you want to access this information somewhere else, you’ll always need to make sure this component is loaded first.
Now with Thunks:
The component logic looks like this:
We need to create a new fetchBreeds action that looks very similar to the logic we previously had in the component:
This simple change of location in the code fixes most of the issues we had previously. We’ve abstracted code from the component and we’ve made this specific piece of logic re-usable throughout the entire code base. This information isn’t bound to mounting the component anymore, so now you can issue a new fetchBreeds action and the data will be loaded.
This also enables us to chain Thunks in case need more complicated logic inside our actions. We can also access the state directly, instead of needing selectors. However, you’ll still want to use selectors to make sure that any changes to Redux don’t affect your Thunks.
Depends on the situation. In the same way you can't use a spoon for everything, the solution has to be chosen based on the problem and not on the other way around.
In the most recent issues I've faced, Thunks was more than enough to satisfy all my edge cases. But maybe in your case, you'll need a more specific solution.
Do your research, keep yourself updated and you'll always have the best tool on your side.
Found this article useful? You might like these ones too!
Your everyday web developer who likes to hide in the backend. Javascript and Ruby are my jam. I still fumble with Docker and my builds break quite often.
People who read this post, also found these interesting: