In React, we have 2 types of state: application state and component state. Component state is the state that only lives within one small building block (called a component), while application state is shared across the whole application (all components). When introducing state, your first thought should be to define it within your component. Only when the state is used in different parts of the application, you mark it as the application state.
In this blog post, we want to give you an overview of the tools that are available today to deal with application state. We call those tools “global state managers”. We’ll talk you through the benefits and possible drawbacks of the different global state managers we already used for our projects. As state is shared over all components and can change over time, the choice of the state managers can have a great impact on your application and how you structure it. So you better make a well-thought-out decision.
We'll compare 4 different state managers:
Context is probably one of the easiest and fastest ways to manage state in React. It’s been part of React since the 0.16.3 version (this was released in March 2018). It was already available before but until then this tool wasn’t recommended. It's an easy stepping stone towards global state management, given the fact that it corresponds to standard state management within a React component.
Context is mainly recommended for data that doesn't require frequent updates. When the dataset increases, context will be less effective for your project.
To quote Sebastian Markbag, Front End Engineer React at Facebook: “My personal summary is that new context is ready to be used for low-frequency unlikely updates (like locale/theme). It's also good to use it in the same way as the old context was used. I.e. for static values and then propagate updates through subscriptions. It's not ready to be used as a replacement for all Flux-like state propagation.”
In addition, context is not made to scale with large applications. It is made for small, infrequently varying datasets within the application, e.g.: theme variable, language, logged-in state...
An example of how to use Context:
Recoil is relatively new in the state management market. It’s created by developers at Facebook who had to deal with performance issues in existing state management tools.
As we already suspected, Recoil is made to use with bigger, more complex data structures. It’s written as if it was a regular React API.
Recoils key features:
Shared data: using the same pieces of data in several separate components
Derived data: edited data across state changes
Recoil works with both atoms and selectors:
Atoms can be seen as pieces of state. Both the selectors as the components can read these atoms to use the data within and to recalculate it when the atom changes. Every atom also has a unique key.
Selectors are functions that an atom or any other selector has as input. They provide us with the possibility to edit data outside of components, which makes that some logic stays out of the components. Selectors can be both synchronous and asynchronous, which makes the library suitable to execute API calls for example.
An example of how to use Recoil:
Redux is a well-known state management tool for web applications. There is a React package (react-redux) available to integrate Redux in React.
Typically, Redux stores your data within nodes. If you structure your application in different feature modules, each module could have its data stored within its own node. The combination of all those nodes is called the Redux store.
Redux has a typical structure with some core concepts: action, reducer, and selector.
An action can be dispatched (e.g. by one of your components) when something happens. This action impacts your application state. Therefore reducers listen to all actions and update their state if necessary. Finally, components listen to state changes through selectors to update the view. Setting up this structure for each action already requires you to write some code. Having to do this for all actions within each module of your application will make you end up with a lot of code.
Before React came with hooks, connecting components to the Redux state was very tedious. No more need for using “mapStateToProps”, “mapDispatchToProps”, and higher-order components. Now the store is easily accessible by using the useSelector (to select state) and useDispatch (to dispatch actions) hooks.
Using Redux is not difficult, but you have to invest some time to grasp this pattern and how the concepts are related to each other. Redux proves to be a very predictable and reliable state management tool that we use in most applications within icapps. It also comes with debugging tools making it possible to view the full Redux store or to see all dispatched actions together with their impact on the current state.
An example of how to use Redux:
Essentially, Mobx is a simple, scalable, and battle-tested state management solution. It works in a reactive, mutative way and when used with React, it does an internal comparison of the state to determine which React components should rerender.
With 24.4k stars on Github [https://github.com/mobxjs/mobx] at the time of writing this blog post, it is a popular choice for state management.
Due to its reactive nature, Mobx is more simplistic but very different from other state management tools like Redux.
You could easily mutate a deeply-nested observable and Mobx would know what relevant components to re-render. This is a lot harder in state management tools that rely on immutable changes, like Redux (although there are libraries like Immer to help you with this), though, we strongly believe the application state should be as normalized and simple as possible. For handling side effects, Mobx has built-in actions and reactions that can be used to manually or automatically update state.
An example of how to use MobX:
To Sum up
Since the choice of state manager has an important impact on the stability of your application, we feel like it’s important to regularly compare and try out new and different state managers available. With this knowledge in mind, we can make the right decision. At icapps, we use Redux as the state manager for 90% of all our projects. Redux is a very reliable and powerful tool to maintain larger, more complex projects. Although Redux has a steep learning curve, it's worth the investment because this state manager is so powerful and widely used. Plus, due to its large scalability, a great amount of data can be handled with Redux.
When we feel the need to isolate some smaller components within the application, components that don’t require many updates, we use Context to manage them separately. The combination of Redux and Context has been successful in our experience. But, we always keep an eye on innovation and we are able to adjust to the requirements of the project and our customers.