Zustand: Forget about other state managers
Few months ago, I had to create a website with 3D models for my client, and I obviously thought about using Three.js in my project. I’ve done some 3D sites before, but never with React, instead plain JavaScript. I searched over the net and found the react-three-fiber library created by a Latin based developer collective Poimandres. I was happy that finally I had a way to declaratively use Three.js. Anyway, during the installation a dependency caught my eye: Zustand. Maybe the odd name made me curious I don’t know, I decided to look up for it and landed to its GitHub page.
Thanks God, I’ve ran into this library later I’ve said. Being a super simple manager, Zustand takes the overwhelming process of state management that we’re used to from Redux. In Redux, we’re nearly forced(!) to use structural hierarchy of store, reducers, actions, constants and the fearful immutability techniques, whereas in Zustand none of them exists. There is a store object which is keeping the state and all your necessary functions and variables are defined altogether in this store. That’s it! You don’t define actions or reducers or whatever separately. Let’s have a closer look what it looks like:
As you can clearly see, the store is created with a hook and it contains reducing functions and state variables. You can put this code at the very beginning of our app, preferably in App.js. The create function includes a set method which allows us to merge the state with the data we want. Zustand is hook oriented, there is no provider context you need to wrap with, which is a big plus considering performance. As your data types increases, you may also want to create hook for each state separately. Say you’re making a blog, you can have useUserStore, usePostsStore, useCommentsStore and so on.
This is really fantastic in terms of simplicity. You code is short, your methods and states are tidy. You can of course take a look at the documentation for advanced usage but I think most of us needs only that much.
Now let’s see how we can use it in a component:
Here we simply call our useStore hook and get the state values as well as the reducer functions. The syntax is identical to useSelector that you’re familiar from React Redux. However there is a tiny difference you might notice; we’re also able to pick the methods from state within this hook. You can also multi-pick state values in a single line as this:
Only thing we need to add here is the shallow helper function that ensures any of the value of the object change, the state is updated, thus the component re-renders. You can of course write your own equality comparison function for your own needs.
One of the major advantage of the library is that you don’t actually need redu-thunk or any thunk-ish library for async operations. I often asked myself why we would need it anyway in first place. You simply define your function async and you’re good to go. Assuming our addTodo function is async, our store would look like.
const useStore = create((set) => ({
todos: [],
addTodo: async (todo) => {
const response = await axios.post('path/to/api', {todo})
set({ todos: [...todos, response.data] })
},
}))
And that’s it! No more thunk or other middleware.
Conclusion
Zustand really made my life easier since I’ve discovered it. I hope it would make yours too. It’s really making no sense to use a whole Redux ecosystem just for example keep a single boolean value in the state. Now those days are gone for me :)
There are couple of other techniques like persisting state, using Immer for immutability but I wanted to keep this article short. You can always check the documentation for those details at: https://github.com/pmndrs/zustand . They’re providing great examples to get you on board.
I also encourage you to follow Poimandres, they are a great community and they did an excellent work with react-three-fiber which I intend to cover in my upcoming articles. Until then;
Happy coding!