When creating a new React application, we think about redux as a state management solution as a default choice. Let’s have a look at the Context API as a possible replacement.
If you’re building a new React app today, your instincts may urge you to choose Redux. But should you start with Redux? That has turned into a legitimate question these days. The state management capabilities of React have vastly increased in recent versions. I’d want to discuss a more straightforward technique that relies on the Context API in this post.
Let’s Take a Look Back
Why Did We Adopt Redux?
It wasn’t always the case that frontend apps used an organized approach to state transitions. When we were still using jQuery, I recall changing many locations in an application. That was not a pleasant encounter.
Here comes Redux to rescue, and in comparison, Redux was an outstanding solution:
- An object represents the whole state of the User Interface (UI).
- Any action that may change the state is a data-contained object.
- To change the state, those acts are ingested by pure functions (reducers)
- We structure the data in the state using selectors so that our components may consume it quickly.
It now appears self-evident. It was, nevertheless, excellent at the time.
All of the factors that got us here remain valid. However, the situation has altered nowadays.
When starting a project, should Redux be the default?
The ecosystem of React has changed a lot recently. Two other additions have altered the terrain and created new opportunities:
It’s easier than ever to manage state across a hierarchical tree and trigger side effects using those two. So simple that I believe Redux is no longer required in many circumstances.
Most frontend apps lack the rich data flow that supports the use of Redux. If all you’re doing is getting data from an API and performing some simple interactions, Redux is overkill now.
Over-abstraction is a pitfall that many people fall into
Forms are a fair instance of something close to elaborating this scenario. I’ve used Redux to create sophisticated forms with the aid of various supporting frameworks. Things become more complicated as they become more complex. And for what purpose? Finally, the state conveyed in a form is usually somewhat local.
Using Redux to model something like a form may be a painful experience. Is the state genuinely global? Frequently, a specialist library will do the job done. For this situation, Formik comes to the rescue. It’s designed specifically for that purpose and will meet almost any criteria you can think of.
Use Context API as an Alternative
From the official docs, we see:
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
The Context API was created to address the prop drilling issue. You have a component with state ownership. It’s been requested by a child component. If you send it through props, it knows every intermediary component, even if it isn’t being utilized at that level. This provides a layer of connectivity between the many components that make up that page section.
Redux has been used to solve the problem of providing state access to components that are located further away from the source. The Context API is a viable solution in this case. One that does not necessitate using a separate library, bootstrapping code, or additional complexity.
How does it work?
The Context API is divided into two parts. We start with a Provider
, which stores the relevant state. It’s the Redux equivalent of the shop. It can be statically initialized or dynamically retrieved. We can quickly write the logic we need to keep it up to date by combining the useState
and useEffect
hooks.
The Consumer
is the second section. Because of it, every component displayed below the Provider
has access to its information. Another hook, useContext
, lightens up the syntax even further. Updates to the state cause consumers to be re-rendered.
We’re replicating the store and connecting to it, just like we would in Redux. We do, however, have a lot less code and abstractions.
First example: High-level configuration
Let’s pretend we’re storing crucial data to the entire app. The theme, for instance, determines how other components are shown. In general, a config has an impact on most components. That is unlikely to change over the application’s lifespan. If we place it in a Provider
, every component that requires it will have easy access to it.
Theming is a classic example because it makes such a straightforward use of Context. It’s also the example given in the official React docs.
Another example: Consuming REST API
Assume you’ve divided your app into subpages, each with its URL. The amount of data that may be sent between pages is restricted. Each subpage functions as a stand-alone program. When you render one of these pages, you’ll need to use an API to get data.
The data, on the other hand, might be utilized anywhere on the subpage. There is no obvious component that may claim ownership of the information. I also enjoy using the Context API to handle this problem. We have a Provider
that encapsulates the page and asynchronously obtains the data from a backend for frontend. This information is saved in the Provider
. Following that, any component on the website may use the data without worrying about remote interaction. As a result, we confine it to a single component that we rigorously verify, allowing the rest to be more self-contained.
The usage of Hooks and Context, as previously stated, dramatically minimizes the complexity. We also avoid dealing with lifecycle methods, which are a typical cause of minor problems.
Reminder: Context API is not for every use case
The Context API is an easy-to-use tool with a minimal learning curve. We must, however, be aware of its limitations. If the data in a Provider
‘s value changes, the entire tree underneath it will be re-rendered. The flickering will occur if you save state that often changes high up in your component hierarchy. It may jeopardize the application’s user experience. Regularly measuring the performance of your application might help you avoid unpleasant surprises.
Aside from that, we need to think about how to change the state. With a function to alter the state, we may improve the state saved in the Provider
. As a result, the components aren’t only passive consumers.
Reducers Without Redux
You may be familiar with Redux’s model by now. Reducer modeling of state transitions has become a well-known problem. Immer is one of the high-quality libraries that support it. The concept of abandoning Redux is both terrifying and revolutionary.
But don’t give up hope. Without utilizing the Redux library, you may profit from Redux’s concepts. The reducer power is brought to you via the useReducer hook. The hook syntax makes the code less thick and easy to read. You continue to employ reducers in the same manner as previously. It also comes with React out of the box, which is a nice bonus.
Conclusion
This post isn’t writing on why you should avoid Redux. Redux’s concepts are as relevant as ever. If your application is sufficiently complex, using Redux makes sense. However, while making an informed selection, it is critical to examine your circumstances. React is constantly evolving, and native state management is becoming easier.