Marcus Nguyen
Software Engineer
Zustand is a minimalistic yet powerful state management library for React applications. Built with simplicity and performance in mind, Zustand offers a lightweight alternative to complex state management solutions while leveraging the benefits of modern TypeScript features. In this blog post, we'll explore how to integrate Zustand into a React application using TypeScript and discuss its benefits for state management.
Zustand is a state management library inspired by the simplicity of Redux combined with the flexibility of React hooks. It provides a simple API for creating and managing global state in React applications without the need for additional dependencies like Redux or MobX. Zustand uses React's built-in context and hooks to manage state in a concise and efficient manner.
To get started with Zustand in your React project, you can install it via npm or yarn:
npm install zustand
To use zustand, we have to import a create function:
import { create } from "zustand";
This function is called with a callback function and it returns a custom hook. The callback function passed to it is where we will define our state and the functions we can use to manipulate the state. The state and the functions are all in an object returned by this callback function.
Let's see an example:
const useCounter = create((set) => {
return {
counter: 0,
incrCounter: () => set((state) => ({ counter: state.counter + 1 })),
};
});
See that the create function, passes a set function to the callback function. This set function is a function used to manipulate the state in the store. States in zustand can be primitives, objects, or functions. In our above example, we have two states in our store: counter, and incrCounter. The useCounter is a custom hook, we can use this hook in our components and we will be able to get the latest state in them. If we use the hook in components A, B, and C. Any change done to the state in B will be reflected in both A and C, and they will all re-render to reflect the new changes.
The custom hook returned by the create acts similarly to useAppSelector in React-Redux, it lets you select a slice of state from the store. You call the hook and pass it a callback function. This function is called by the hook internally and passes the current state to it. So we will then get this state and return the part of the state we want.
Let's see an example.
const counter = useCounter((state) => state.counter);
See that we called the useCounter hook and passed a callback function to it. Then, we expect a state from the hook and then return the counter part of the state.
We can then, display the counter:
const DisplayCounter = () => {
const counter = useCounter((state) => state.counter);
return <div>Counter: {counter}</div>;
};
Now, we want to create a component where we can increase the value of the counter state.
const CounterControl = () => {
const incrCounter = useCounter((state) => state.incrCounter);
return (
<div>
<button onClick={incrCounter}>Incr. Counter</button>
</div>
);
};
This is a separate component from where we increase the value of the counter state. See that we sliced out the incrState function from the state, and we set it to the onClick event of the button. This will increase the counter state when the button is clicked. See how the components are independent of each yet they can "see" the current state from the store. Whenever we click the Incr. Counter button in the CounterControl component, the DisplayComponent will re-render to display the newest counter state value.
Let's see how we use them:
const App = () => {
return (
<>
<DisplayCounter />
<CounterControl />
</>
);
};
They are independent of each other yet magically connected by zustand. This gives React-Redux a run for its money because trying to re-create this small state in Redux-React will take more code to set up:
It's quite lengthy, but with Zustand it's oversimplified.
Returning the whole state Now, when we call the custom hook returned by the create function without a callback function, the hook will return the whole state of the store.
const state = useCounter();
See that we called the useCounter hook with no callback function, so in this case, the function will return the whole state in the store.
The state holds the whole state in the useCounter store. We can get the counter state by doing this:
state.counter;
// 0