Article summary
I recently started a new project from scratch using Vite with React. After adding some routes and simple starting components, I quickly realized I needed another way to pass around data, other than React props. To satisfy this need for additional state management, Redux was an obvious choice. However, I’ve worked with Redux multiple times and find it verbose and complicated. Therefore, I decided to first consult the React roadmap for additional options; that’s when I discovered Zustand.
It’s easier to get going.
One of the main benefits of Zustand over Redux is that it’s simple to understand, with minimal boilerplate code. The following code snippets are provided as examples on the Zustand official website. They show how the Zustand store can contain primitives, objects or functions.
import { create } from 'zustand'
const useBearStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
Finally, to interact with the store, you can do the following:
function BearCounter() {
const bears = useBearStore((state) => state.bears)
return {bears}
}
function Controls() {
const increasePopulation = useBearStore((state) => state.increasePopulation)
return {increasePopulation}
}
To access the store (which is just a hook), you simply import it and grab the item (primitive, object, function) off of that store with which you want to interact. See state.bears
and state.increasePopulation
. The component will then re-render on any store changes. To understand Zustand, you need to understand hooks, which are very familiar to React developers, as well as the basic primitives of programming languages. Therefore, the only “new” information needed to start using Zustand is to understand that the store is just a hook.
On the other hand, to begin using Redux, you’ll require an understanding of the terminology of reducers, actions, and dispatches. For instance, take a look at the following tutorial code from the official Redux website:
import { createStore } from 'redux'
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
let store = createStore(counterReducer)
store.subscribe(() => console.log(store.getState()))
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}
The reducer function takes the current state and an action describing what happened and then returns the updated state. To mutate the store, you must then call store.dispatch
with the action to be taken. Compared with the Zustand example code, it is already clear that Zustand provides a simpler way to interact with the store. Why have all this indirection and opinionated language around your state management solution, when you could simply be dealing with hooks and programming primitives?
Hooks are awesome.
As we’ve seen in previous examples, Zustand makes hooks the main method of accessing the store. You only need one hook, the useStore()
hook. In the above Redux example, you wouldn’t normally subscribe to the store to get the state (i.e., store.subscribe(() => console.log(store.getState()))
), but would instead use additional tooling such as React Redux. See the following portions of code taken from the React Redux official website:
import { useSelector, useDispatch } from 'react-redux'
import {
decrement,
increment,
incrementByAmount,
incrementAsync,
selectCount,
} from './counterSlice'
const count = useSelector(selectCount)
const dispatch = useDispatch()
You can then call count
to display the value of count and dispatch(increment())
to increment the value of count
. Ultimately, React Redux is also using hooks in a very effective manner, but two hooks are needed, (useSelector()
and useDispatch()
), where a single hook does the job for Zustand (useStore()
). Again, it is better to reduce complexity and learn how to use Zustand’s single hook than it is to learn the jargon of Redux’s selector and dispatch hooks.
Seek Out Redux Alternatives
There are better React state management solutions than Redux. Before blindly reaching for Redux, consider Zustand for its simpler interface and straightforward hook-based approach. Additionally, before even trying out Zustand, consider if React context would be sufficient. Not every application will need an explicit state management solution. Developer quality of life and code complexity should be the priorities when choosing a state management solution, not familiarity or popularity.