What's inside
In the previous article we covered the Redux, Redux Toolkit, Context, and Recoil.
Now, we would like to focus on Jotai, XState, Mobx, Zustand, and React Query. Let’s start!
Jotai
Jotai is a simple, atom-based state management library for React. It is lightweight - as mentioned earlier Jotai is much smaller than Recoil (3,3 kb vs. 14kb, but it’s considered a direct competitor to it. It is also straightforward in a way of managing state in React application.
The API is very similar to Recoil because Jotai is inspired by Recoil but unlike Recoil, Jotai does not require passing a string key. It provides a minimalistic API with TypeScript readiness, It also supports selectors.
The documentation is more transparent than the one in Recoil which uses some tool to persist it’s data (but was never open sourced).
import { Provider, atom, useAtom } from 'jotai'
const textAtom = atom('hello')
const textLenAtom = atom((get) => get(textAtom).length)
const uppercaseAtom = atom((get) => get(textAtom).toUpperCase())
const Input = () => {
const [text, setText] = useAtom(textAtom)
return <input value={text} onChange={(e) => setText(e.target.value)} />
}
const CharCount = () => {
const [len] = useAtom(textLenAtom)
return <div>Length: {len}</div>
}
const Uppercase = () => {
const [uppercase] = useAtom(uppercaseAtom)
return <div>Uppercase: {uppercase}</div>
}
const App = () => (
<Provider>
<Input />
<CharCount />
<Uppercase />
</Provider>
)
export default App
Jotai library is one of the lightest state management libraries currently available. Considering all its features it seems like a perfect candidate for state management. Certainly, it is a good choice for beginners for state management in React thanks to the simplicity of the library.
XState
XState differs from other state managment libraries because its more about creating flow of the application. XState can be used with React to coordinate local state, manage global state and consume data from other hooks. It applies concept of state machine.
I think the internet of things is a good application of this idea but it also can be applied to e-commerce web applications (with payment flow, or authentication flow), and also it can be good for applications written as points of sale.
The business logic is very easy to follow when you use XState.
Check the example from XState documentation, shows the state visualizer tool which helps build state machine diagrams using XState and some code sample here.
import { createMachine, interpret } from 'xstate';
// Stateless machine definition
// machine.transition(...) is a pure function used by the interpreter.
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
// Machine instance with internal state
const toggleService = interpret(toggleMachine)
.onTransition((state) => console.log(state.value))
.start();
// => 'inactive'
toggleService.send('TOGGLE');
// => 'active'
toggleService.send('TOGGLE');
// => 'inactive'
MobX
MobX is a simple, scalable and boilerplate-free state management tool, to manage the state of React application. MobX is similar to Redux but it uses and implements observable values. In contrast to Redux you can have multiple stores.
If you are looking for an answer what is better - MobX or Redux - well… based on popularity, and on the general saying of developer community - Redux performs better BUT if you want to quickly get up to speed and build simple apps with less boilerplate code - MobX should be your choice.
To sum up,if you are looking for a tool to develop the application fast, go for MobX. Redux is mainly used to develop apps which are more complex, and usually it takes more time.
import * as React from "react"
import { render } from "react-dom"
import { observer } from "mobx-react-lite"
const TodoListView = observer(({ todoList }) => (
<div>
<ul>
{todoList.todos.map(todo => (
<TodoView todo={todo} key={todo.id} />
))}
</ul>
Tasks left: {todoList.unfinishedTodoCount}
</div>
))
const TodoView = observer(({ todo }) => (
<li>
<input type="checkbox" checked={todo.finished} onClick={() => todo.toggle()} />
{todo.title}
</li>
))
const store = new TodoList([new Todo("Get Coffee"), new Todo("Write simpler code")])
render(<TodoListView todoList={store} />, document.getElementById("root"))
Zustand
Zustand is the simplest and smallest of all the available libraries that help to manage your state.
Following the official documentation - on their website you can see a very simple example of a globally available state:
import create from 'zustand'
const useStore = create(set => ({
bears: 0,
increasePopulation: () => set(state => ({ bears
}))
import create from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
Create is the main function of the state management library, and it then returns another function called useStore. It returns a Hook, and you can use the Hook anywhere, no providers needed. You can also select your state and the component will re-render on changes.
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} around here ...</h1>
}
function Controls() {
const increasePopulation = useStore((state) =>
state.increasePopulation)
return <button onClick={increasePopulation}>one
up</button>
}
As you can see Zustand is a library that tries to handle the global states problem in a more pragmatic and simple way.
Citing the original documentation:
1. Why Zustand over Redux?
- Simple and un-opinionated
- Makes hooks the primary means of consuming state
- Doesn't wrap your app in context providers
- Can inform components transiently (without causing render)
2. Why zustand over Context?
- Less boilerplate
- Renders components only on changes
- Centralized, action-based state management”.
There is also an option to select multiple state slices. It detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks.
const nuts = useStore((state) => state.nuts)
const honey = useStore((state) => state.honey)
React Query
Last but not least, we can't forget about React Query. It’s not a library for global state management but can be quite useful in some cases. For example, you have to synchronize single views with state from the server. It is often paired with Context APIs.
React-Query performs pre-fetching so the applications can update stale data. You just need to tell the library where you’d like to fetch your data, and it will take care of catching, background updates, and stale data without any extra work like coding or configuration. It also has selectors, and really easy to use hooks.
The main hook you can use, provides data as - isLoading, error, data - so it’s kind of easy to fetch data, and display some loader when data is still being fetched (or error if you cannot fetch).
There is a similar library working with Redux Toolkit called RTK-Query.
import { QueryClient, QueryClientProvider, useQuery } from 'react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
function Example() {
const { isLoading, error, data } = useQuery('repoData', () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res =>
res.json()
)
)
if (isLoading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong> {data.subscribers_count}</strong>{' '}
<strong> {data.stargazers_count}</strong>{' '}
<strong> {data.forks_count}</strong>
</div>
)
}
Check it here in more details.
Bottom line is…
There is no right or wrong answer to the question “Which tool is the best”. There is no best global state management tool. The only thing we can say is “IT DEPENDS”.
It all depends on needs.
If you need something for big, scalable applications, Redux might be the best choice. It has a lot of resources and the most significant community. A small library like Zustand makes your entire global state a custom hook, and on the other hand, is simpler and un-opinionated.
Regarding smaller applications Jotai, Recoil or Mobx are worth trying.
If you want a clear view of business logic in the application and follow some specific flow with some final state you should go for XState then.
If you aim to synchronize views with server data (one view per page), you probably don’t need global state management but a tool that could help you fetch and cache the data like React Query.
The best choice is simply using what works for you. At the same time remember to keep an eye on what's going on out here - you have never known, and just in case something better comes around.
In the first part of the article, we covered Redux, Redux Toolkit, Context, and Recoil.
So, if you want information about these tools, read more about them here.