DEV Community

Cover image for A Beginner’s Guide to State Management in React with Zustand
Aravindh_dev
Aravindh_dev

Posted on

A Beginner’s Guide to State Management in React with Zustand

Introduction

State management is crucial for any React application, but traditional libraries like Redux can sometimes feel like overkill. Enter Zustand, a minimal and powerful state management solution for React. In this post, we’ll dive into why Zustand is becoming a favorite for developers and how to get started with it in your React projects.

What is Zustand?

Zustand is a state management library for React that is designed to be simple and intuitive. It is lightweight and doesn't require a lot of boilerplate, which makes it easier to use than Redux or even the React Context API. Let's take a look at how we can use Zustand in our React applications.

Setting Up Zustand in React

  • Install Zustand

    npm install zustand
    
  • Create a Store
    Here's a simple example of how to create a store in Zustand:

    import {create} from 'zustand';
    
    const useStore = create((set) => ({
      count: 0,
      increase: () => set((state) => ({ count: state.count + 1 })),
      decrease: () => set((state) => ({ count: state.count - 1 })),
    }));
    
  • Using the Store in a Component
    Now, let's use the store in a React component:

    import React from 'react';
    import { useStore } from './store';
    
    const Counter = () => {
      const { count, increase, decrease } = useStore();
    
      return (
        <div>
          <h1>{count}</h1>
          <button onClick={increase}>Increase</button>
          <button onClick={decrease}>Decrease</button>
        </div>
      );
    };
    
    export default Counter;
    

Advanced Zustand Features: get, getState

  • Zustand also provides two other useful functions: get and getState. These are used to retrieve the state and get the state at any point in time

getState(): This function gives you the current state of the store without triggering a re-render.

import {create} from 'zustand';

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

// Accessing the current state using getState()
const count= useStore.getState().count;

// Reading the current state value
console.log(count); // This will log the current count

// Modifying the state using the actions
store.increase(); // This will increase the count
console.log(store.count); // This will log the updated count
Enter fullscreen mode Exit fullscreen mode

get(): This function allows you to directly access state from within the store itself. It’s useful if you need to check or modify state before or after setting it.

import {create} from 'zustand';

const useStore = create((set, get) => ({
  count: 0,
  increase: (amount) => {
    const currentState = get(); // Access the current state using getState()
    console.log("Current count:", currentState.count); // Log the current count
    set((state) => ({ count: state.count + amount })); // Modify the state
  },
})); 
Enter fullscreen mode Exit fullscreen mode

Slices in Zustand

  • As your application grows, it’s a good idea to organize your state into smaller, more manageable pieces. This is where slices come into play. A slice is a modular piece of state with its own set of actions. Each slice can be defined in its own file, making your code cleaner and more maintainable.
// counterStore.js
export const createCounterSlice = (set) => ({
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })),
  decrease: () => set((state) => ({ count: state.count - 1 })),
});

Enter fullscreen mode Exit fullscreen mode
// userStore.js
export const createUserSlice = (set) => ({
  user: { name: 'John Doe' },
  setName: (name) => set({ user: { name } }),
});

Enter fullscreen mode Exit fullscreen mode
// useBoundStore.js
import {create} from 'zustand';
import { createCounterSlice } from './counterStore';
import { createUserSlice } from './userStore';

export const useBoundStore = create((...a) => ({
  ...createCounterSlice(...a),
  ...createUserSlice(...a),
}));
Enter fullscreen mode Exit fullscreen mode

How to use inside component

import { useBoundStore } from './useBoundStore'
const App = () => {
  const { count, increase, decrease, user, setName } = useBoundStore();
}
Enter fullscreen mode Exit fullscreen mode

Persisting State in Zustand

  • Zustand's persist middleware automatically saves the state to localStorage when it changes and loads it back when the page is reloaded, making sure the state stays the same without needing extra work.
import {create} from 'zustand';
import { persist } from 'zustand/middleware';

const useStore = create(
  persist(
    (set) => ({
      count: 0,
      increase: () => set((state) => ({ count: state.count + 1 })),
      decrease: () => set((state) => ({ count: state.count - 1 })),
    }),
    {
      name: 'counter-storage', // The name of the key in localStorage
    }
  )
);

Enter fullscreen mode Exit fullscreen mode

Fetching Data from an API in Zustand

  • To fetch data from an API in Zustand, create an action within your store to handle the API call and update the state with the fetched data, loading, and error states.
import {create} from 'zustand';

const useStore = create((set) => ({
  users: [], // Array to store fetched users
  loading: false, // State to track loading status
  error: null, // State to track any errors during API call

  // Action to fetch users from the API
  fetchUsers: async () => {
    set({ loading: true, error: null }); // Set loading state to true and reset error
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/users');
      const data = await response.json();
      set({ users: data, loading: false }); // Set users data and loading to false
    } catch (error) {
      set({ error: 'Failed to fetch users', loading: false }); // Set error if fetch fails
    }
  },
}));

export default useStore;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)