2 April, 2020
Managing states in a React application is one of the biggest challenges for many frontend developers. In large commercial web applications, the complexity may be such that using React alone will not be sufficient. Some developers tackle the challenge by using React Hooks, others combine them with application state management libraries like Redux. Today, we’re going to take a closer look at both hooks and Redux in the context of managing state, complete with practical code examples.
In the recent article, we have talked about the React component lifecycle, which was a good opportunity to mention hooks – a relatively recent addition to the React library, which makes it possible to add state to functional components. But merely creating stateful functional components doesn’t address all the challenges with state in React. Why? Let’s take a look.
React state management – why is it so difficult?
Let’s say we’re building a simple video sharing and viewing app similar to YouTube. In an app like this, just about every element can be a component – the video screen, the buttons, the comment section, lists, login bar etc. In this app, just a single user action of viewing a video can influence many other components by:
- changing the state of the video component itself,
- adding the video to the user’s history,
- triggering the view count.
And that’s just the tip of the iceberg! If the programmers didn’t have scalability in mind when creating the app, they may have a really hard time figuring out what’s going on when something goes wrong. Maintaining and debugging an app like this could potentially be a nightmare.
Redux to the rescue
And why is often Redux used specifically with React? First, since Redux handles state updates in response to actions, especially user actions in the UI, it works best with libraries that describe UI as a function of state. But there is more.
Despite the fact that Redux can be used with any UI layer or framework and both React and Redux are independent of each other, they are commonly used together for one simple reason.
“It was originally designed and intended for use with React” – says React Redux docs.
And we believe it’s the main cause of the fact that we use Redux with React. While there are different UI bindings (the way the UI is connected to the states) for Redux, the React Redux binding is maintained by the Redux team. What does that mean? That it is always kept up-to-date. When any of the libraries are updated, we can be sure that it will still behave as expected.
State & hooks
How to manage application state with React Hooks and Context? See the example below where we use the useReducer hook.
First, create Context and define reducer in contexts/todo-context.js.
Then, wrap components in Provider. For the sake of simplicity, I’ve done that in App.js.
Finally, use the Context and display the state in components/Todo.js.
You can create your own custom hooks to avoid repetition and make code cleaner.
In this example, we’re creating the useWindowSize hook.
React state management with hooks and React
Let’s do similar actions with Redux. Let’s start by creating the store, actions and reducers.
Store in App.js.
Actions in actions/index.js.
Reducers in reducers/index.js.
In the components/Todo.js, create map functions and use connect as Higher Order Component.
Rest is nearly the same. Take a look at the whole file:
Hooks and Redux – friends, not enemies
Redux and React hooks are complementary. React Redux offers a set of hooks which can be used as an alternative for connect().
useSelector() allows you to extract data from the Redux store state:
useDispatch() returns a reference to the dispatch function from the Redux store:
useStore() returns a reference to the Redux store, but you shouldn’t use that too often. Use useSelector if you need values from state. This can be used for example for replacing reducers.
It should be quite clear right now, but it’s worth it to stress again that hooks and Redux are definitely not in each other’s way. In fact, they overlap marginally. Due to React 16.8 hooks (especially useState, useReducer, useContext) you often may not need Redux, but when it comes to really complicated and expanded state, you should use that for code predictability.
For small to mid-sized apps, it’s really good to use Context. Thanks to the useContext hook, you can provide information to the global store in a bidirectional manner and in a somehow nicer and cleaner way than the old Context API. Remember, leaving Redux in favour of React Context means that you lose integration with Redux DevTools, which is a really useful tool for debugging state issues.
Besides, you can use both simultaneously with the help of the useReducer hook and React Redux hooks. It works great! Take into consideration the fact that React Hooks replace class components and the render prop pattern and despite using them for state updates, it was always a React feature, even before React 16.8. And Redux has already existed and still exists, fitting nicely into this state model.
As you can see, React state management, with all its challenges, can actually be quite a breeze if you incorporate this topic into the very design of your application from the start. Both hooks and Redux can help you get there. Most of the time, you will want to use both to address state management and scalability challenges in complex commercial web applications.
And if you’d like to find React developers who can already do all that and more, learn how to acquire and retain top React developers.