Skip to main content

Command Palette

Search for a command to run...

Decoding Zustand: Simplifying State in React

Published
5 min read
Decoding Zustand: Simplifying State in React
D

I am a Software Engineer with 3+ years of experience, currently at P360. I have a passion for creating intuitive web interfaces and actively contribute to tech communities as the Organizer of GDG Siliguri, ex-Microsoft Learn Student Ambassador, and former Hack Club Lead. As a tech speaker, I’ve presented at events like FrontendDays Siliguri, GDG Bhopal, and Azure DevDay. I’m also passionate about hackathons and open-source: Smart India Hackathon 2020 winner, HacktoberFest contributor, and mentor to the winning team of SIH 2022.

Introduction

If you are a React Developer, you are already familiar with the topic state management. In the State Management world, Redux is quite popular. But, sometimes, Redux is frustrating because of its lengthy boilerplate code. In places like that, Zustand comes as a saviour.

What is Zustand?

Zustand is a state management library for React that offers a minimalistic yet powerful API. It provides a simple way to manage the state of your application without the need for complex abstractions.

Getting Started with Zustand

To use Zustand in your existing React project, use the following command in the terminal: If you are using npm:

npm install zustand

If you are using yarn:

yarn add zustand

Next, let's create a store to save the global state data. If you are familiar with the basics of the react-redux, you will understand it easily. Let's add a folder named store in your React project and add an store.js file to it. Now, let's create a store in the file, to save global state data. To create a store, we are using the create function from Zustand. In Zustand, everything is hook-based. So we are naming it as useStore hook.

import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: (qty) => set((state) => ({ count: state.count + qty })),
  decrement: (qty) => set((state) => ({ count: state.count - qty })),
}));

export default useStore;

In this example, we have created a simple store to save the value of the count. Here, the set function is the state setter function, which helps us to set new values. Also, increment and decrement are the actions to change the state. Now, as our store is created, let's add it to our components.

import useStore from './useStore';

const App = () => {
  const { count, increment, decrement } = useStore();

  return (
    <div>
      <h1>Counter App</h1>
      <p>Count: {count}</p>
      <button onClick={() => {increment(1)}}>Increment</button>
      <button onClick={() => {decrement(1)}}>Decrement</button>
    </div>
  );
};

export default App;

To use the store, we just need to import the useStore hook. And that will do all the tasks for you. You can import the increment, decrement and count from it. And use it anywhere in your code.

Devtools and Persist

Now, you may ask that in Redux, you are using Redux Devtools, in Zustand, how to use it? We can use that in Zustand also. Just import the devtools from the zustand middleware.

import create from 'zustand';
import { devtools } from 'zustand/middleware';

const useStore = create(
  devtools((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
  }))
);

export default useStore;

Now, you can open the Redux Devtools and use it with Zustand.

Sometimes, we want to persist the data in the Local Storage or Session Storage. In Zustand, that is also pretty easy using the persist middleware.

import create from 'zustand';
import { devtools, persist } from 'zustand/middleware';

const useStore = create(
  persist(
    devtools((set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
      decrement: () => set((state) => ({ count: state.count - 1 })),
    })),
    {
      name: 'counter-app',
    }
  )
);

export default useStore;

Zustand using TypeScript

To use with TypeScript is also pretty easy. You can define the types or interfaces first and then refer to them in the create function.

import { create } from 'zustand'

type State = {
  count: number
}

type Actions = {
  increment: (qty: number) => void
  decrement: (qty: number) => void
}

const useCountStore = create<State & Actions>((set) => ({
  count: 0,
  increment: (qty: number) => set((state) => ({ count: state.count + qty })),
  decrement: (qty: number) => set((state) => ({ count: state.count - qty })),
}))

Use Zustand in Redux way

If you like the structure of Redux, you can also generate that in Zustand. We can create an action named dispatch to handle all the store updates.

import { create } from 'zustand';
import { devtools } from 'zustand/middleware';

type State = {
  count: number;
};

type Actions = {
  dispatch: (action: Action) => void;
};

type Action = {
  type: 'increment' | 'decrement';
  qty: number;
};

const counterReducer = (state: State, action: Action) => {
  switch (action.type) {
    case 'increment': {
      return {
        count: state.count + action.qty,
      };
    }
    case 'decrement': {
      return {
        count: state.count - action.qty,
      };
    }
    default: {
      return state;
    }
  }
};

const useStore = create<State & Actions>()(
  devtools((set) => ({
    count: 0,
    dispatch: (action: Action) => set((state) => counterReducer(state, action)),
  }))
);

export default useStore;

Immer Integration

Zustand seamlessly integrates with Immer, a library that makes working with immutable state easy and intuitive. This allows you to update your state in a mutable way while ensuring immutability under the hood. Let's say you have a deeply nested object in your store. Now, you want to update the count, which is a deeply nested entity. Normally, you can do that like this:

  increment: () =>
    set((state) => ({
      deep: {
        ...state.deep,
        nested: {
          ...state.deep.nested,
          obj: {
            ...state.deep.nested.obj,
            count: state.deep.nested.obj.count + 1
          }
        }
      }
    })),

Using Immer, it will just be a one line code, like this:

  increment: () =>
    set(produce((state: State) => { ++state.deep.nested.obj.count })),

Conclusion

Zustand is a powerful yet lightweight state management library for React, offering a simple API with excellent performance. Whether you're working on a small project or a large-scale application, Zustand's flexibility and ease of use make it a compelling choice for managing state in your React applications. If you like this blog and want to learn more about Frontend Development and Software Enginnering follow me on hashnode.

H
Hashbyt8mo ago

Thanks for this insightful guide, Debajit ! Zustand’s flexibility aligns perfectly with the 'Frontend-First' philosophy we champion at Hashbyt where the UI isn’t just a design layer but the growth engine of modern SaaS products.

I’m curious, how do you see Zustand scaling in enterprise-grade applications with complex state requirements? We’ve found that pairing Zustand with tools like Immer and server-side rendering frameworks like Next.js can unlock incredible performance gains.

B
Bobo1y ago

Your blog is a great resource! I always find your discussions about API testing helpful. EchoAPI has become an essential part of my React development toolkit, enhancing collaboration and efficiency.

More from this blog

D

Debajit Mallick's Blog

19 posts

I am a Software Engineer with 2+ years of experience. Currently, I am working at P360 as a Software Engineer. My expertise is in Frontend Web Development.

I am very active in technical communities. I am the Organizer of GDG Siliguri, Ex β-MLSA, Ex Hack Club Lead, and Ex GSSOC Ambassador.

Also, I am a Tech Speaker too. I have given technical talks at many events like FrontendDays Siliguri, GDG Bhopal, GDSC WOW KOLKATA, JWOC, Azure Devday, Hack Club SIT, GirlScript Siliguri, GDSC SIT, Codecademy Frontend Marathon and many more.

I also like to participate in Hackathons and Open Source events. I won the Smart India Hackathon 2020 and contributed to HacktoberFest 2019, HacktoberFest 2020, JWOC and GWOC. Also, I mentored a team for Smart India Hackathon 2022 that later won the Hackathon.