Simplifying State Management in React
Introduction
In our Next.js project, we used the built-in React.Context
for managing state. However, as our project grew, the simplicity we initially enjoyed became somewhat of a headache. To add even a simple feature to one component, we had to create a context, hooks, add a provider, and wrap the entire app with it. I mean...
This article shares our adventure in testing different state management tools and picking the best one for us.
Initially, we needed to conduct research to discover our options.
Requirements
Before we started looking for a solution, we had to define what we were looking for. Here are the requirements we had, that can be applied to any other type of library we would choose (we used the same when we were looking for sliders-library):
- TypeScript support
In our project we useTypeScript
, so it was crucial to have a library that supports it. - Performance
We wanted to have a library that will make our app faster, or at least not slower. - Browser Support
The state manager should offer compatibility with the targeted browsers for our project. - Lightweight
As an additional library, it shouldn't significantly impact the app's bundle size. - Supported
It should be maintained, so we don't have to look for another library after another React release. - Community
Does it have enough answered questions on StackOverflow? 😁 - Developer experience
The syntax should be clear and understandable, it should align closely with our existing code style. The library should be well documented.
After a quick investigation, we decided to compare MobX
, Zustand
, Jotai
, Valtio
, and Recoil
. Those are the most popular libraries right now.
Investigation
MobX
MobX
relies on the concept of observables and reactions. Observables are the state that can be observed, and reactions are the side effects that are triggered when the state changes.
Pros:
- It's easy to use and has a simple API.
- It's a good choice for applications with a lot of dynamic state.
- It has a large community and is well supported.
Cons:
- It has a larger bundle size than other libraries.
- It can also be slower than other libraries when working with large amounts of data.
- It uses proxy objects, which are not supported in some browsers.
Code example:
import { observable, action } from "mobx";
import { observer } from "mobx-react-lite";
const appState = observable({
count: 0,
increment: action(function () {
this.count++;
}),
});
const MyComponent = observer(() => {
return (
<div>
Count: {appState.count}
<button onClick={appState.increment}>Increment</button>
</div>
);
});
Zustand
Zustand
is a small, fast, and scalable state management library with simplified flux principles.
Pros:
- It has a small bundle size.
- It's easy to use and has a simple API that is based on hooks.
- Good choice for small to medium-sized applications that need to manage complex state.
Cons:
- Not as feature-rich as other libraries.
- Smaller community and ecosystem compared to MobX.
Code example:
import create from "zustand";
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
const MyComponent = () => {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
return (
<div>
Count: {count}
<button onClick={increment}>Increment</button>
</div>
);
};
Jotai
Jotai
is a state management library that uses atoms to manage state. It has component-centric approach to state management in React applications. It focuses on breaking down the application state into smaller, independent pieces called atoms.
Pros:
- It has a small bundle size.
- It's easy to use and has a simple API that is based on hooks.
- Good choice for small to medium-sized applications that require predictable state management and reusability of state.
Cons:
- Smaller community and ecosystem compared to MobX and Zustand.
- It may have a steeper learning curve than other libraries due to its more advanced features like atoms and derived state.
- Atomic approach to state management may not be suitable for applications with complex state management needs, where the state may have complex relationships or dependencies.
Code example:
import { atom, useAtom } from "jotai";
const countAtom = atom(0);
const MyComponent = () => {
const [count, setCount] = useAtom(countAtom);
const increment = () => {
setCount(count + 1);
};
return (
<div>
Count: {count}
<button onClick={increment}>Increment</button>
</div>
);
};
Valtio
Valtio
is a state manager that uses the proxy pattern for managing state using the Proxy
object. It provides a simple and efficient way to manage state in React applications.
Pros:
- It has a small bundle size.
- It's easy to use and has a simple API that is based on hooks.
- Good choice for small to medium-sized applications that need to manage complex state.
Cons:
- It uses the
Proxy
object, which is not supported in some browsers. - May not be suitable for applications that require a high level of performance or handle large amounts of data.
- Not really popular and has the smallest community compared to other choosen libraries.
Code example:
import { proxy } from "valtio";
const state = proxy({ count: 0 });
const MyComponent = () => {
const count = state.count;
const increment = () => {
state.count++;
};
return (
<div>
Count: {count}
<button onClick={increment}>Increment</button>
</div>
);
};
Recoil
Recoil
is an experimental state management framework presented by Facebook. It is based on the concept of "atoms" (as Jotai). Recoil allows to create a data-flow graph that flows from atoms through selectors and down into React components. Atoms are units of state that components can subscribe to. Selectors transform this state either synchronously or asynchronously.
Pros:
- Support for asynchronous state updates.
- It's a Facebook project, so it has a good chance to be supported and maintained.
Cons:
- It has a larger bundle size than other libraries.
- It may have a steeper learning curve than other libraries due to its more advanced features like atoms and selectors.
- It is still in the experimental stage and may not be suitable for production applications.
- It has a smaller community and ecosystem compared to MobX and Zustand.
Code example:
import { atom, useRecoilState } from "recoil";
const countState = atom({
key: "countState",
default: 0,
});
const MyComponent = () => {
const [count, setCount] = useRecoilState(countState);
const increment = () => {
setCount((prevCount) => prevCount + 1);
};
return (
<div>
Count: {count}
<button onClick={increment}>Increment</button>
</div>
);
};
Comparison
To differentiate those libraries we read a couple of articles, compared basic features and looked at the community.
Since Recoil
and Mobx
have significantly larger bundle sizes than the others, we decided to exclude them from the competition.
We also excluded Valtio
, because it has the smallest community and is not as popular as the others.
It left us with Zustand
and Jotai
.
Both of them are relatively new, but have a lot of potential. They are lightweight, support TypeScript
and are actively supported. So we decided to compare them in more details and to try each of them in our project.
Jotai
is a great option and has some unique features, but it may have a steeper learning curve than Zustand
due to its reliance on React hooks and more advanced features like atoms and derived state.
And Zustand
is known for its simplicity and ease of use. It uses a functional programming approach which makes it easy to reason about and test, and it provides a straightforward API for creating and updating global state.
Zustand
is not much older than Jotai
but has a community that's twice as large..
Although it was mentioned that Jotai
has more features, Zustand
’s features are sufficient for our project. No need to choose a richer library just because it is richer.
Conclusion
Are we happy with Zustand
? So far, yes. We re-wrote some existing contexts with Zustand
and our code became cleaner. Did we get rid of React.Context? No, and we're not going to. We still use it for some cases, but Zustand is a great addition to our project.
Useful links
- To know and understand state management categories in React - State Management in React
- To compare info and code examples of chosen libraries - Compare NPM packages
Yaroslava Mashnenko
Software Engineer