- Published on
Zustand State Management library for React Application
- Authors
- Name
- Yinhuan Yuan
Introduction
Zustand is a lightweight state management library for React that makes it easy to create and manage global state. Here’s how you can set up and use Zustand to create global state management in a React application:
Step-by-Step Guide to Using Zustand
Install Zustand: Begin by installing Zustand in your React project.
npm install zustand
or with Yarn:
yarn add zustand
Create a Zustand Store: Create a new file, for example,
useStore.js
, to define your global state store.import { create } from 'zustand' // Create the store with initial state and actions const useStore = create((set) => ({ // Initial state count: 0, user: null, // Actions increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), setUser: (user) => set({ user }), })) export default useStore
Access the Store in Your Components: Import and use the store in your React components to read or update the global state.
import React from 'react' import useStore from './useStore' function Counter() { // Access state and actions from the store const { count, increment, decrement } = useStore() return ( <div> <h1>Count: {count}</h1> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ) } export default Counter
Using the Store with Other Components: You can use
useStore
in multiple components without needing to pass props down or lift the state up.import React from 'react' import useStore from './useStore' function UserProfile() { const { user, setUser } = useStore() const loginUser = () => { const sampleUser = { name: 'John Doe', age: 30 } setUser(sampleUser) } return ( <div> <h2>User: {user ? user.name : 'No user logged in'}</h2> <button onClick={loginUser}>Log In User</button> </div> ) } export default UserProfile
Advanced Tips for Zustand:
Persisting State: To persist state across page reloads, you can use
zustand/middleware
with localStorage.import { create } from 'zustand' import { persist } from 'zustand/middleware' const useStore = create( persist( (set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), }), { name: 'counter-storage', // unique name } ) ) export default useStore
Selectors for Better Performance: You can use selectors to read specific pieces of state to optimize re-renders.
const count = useStore((state) => state.count) const increment = useStore((state) => state.increment)
Combining Multiple Stores: For more complex apps, consider splitting state into multiple stores and combining them as needed.
DevTools Integration: For better debugging, Zustand can be integrated with Redux DevTools.
import { create } from 'zustand' import { devtools } from 'zustand/middleware' const useStore = create( devtools((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), })) ) export default useStore
Middlewares
Zustand has a flexible API that supports middlewares to extend and enhance store capabilities. Here are some commonly used middleware options for Zustand:
persist
:
1. Purpose: Persists the store's state to
localStorage
orsessionStorage
, allowing the state to be retained across page reloads.Example:
import { create } from 'zustand' import { persist } from 'zustand/middleware' const useStore = create( persist( (set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), }), { name: 'counter-storage', // unique storage key } ) )
devtools
:
2. Purpose: Integrates Zustand with the Redux DevTools for better state debugging.
Example:
import { create } from 'zustand' import { devtools } from 'zustand/middleware' const useStore = create( devtools( (set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), }), { name: 'MyStore', // Name for DevTools label } ) )
combine
:
3. Purpose: Helps combine initial states and actions for better organization of complex stores.
Example:
import { create } from 'zustand' import { combine } from 'zustand/middleware' const useStore = create( combine({ count: 0 }, (set) => ({ increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), })) )
immer
:
4. Purpose: Uses Immer to allow writing state updates in a mutable style while maintaining immutability under the hood.
Example:
import { create } from 'zustand' import { immer } from 'zustand/middleware' const useStore = create( immer((set) => ({ count: 0, increment: () => set((state) => { state.count += 1 }), })) )
redux
:
5. Purpose: Provides a way to use Redux-style reducers and actions with Zustand.
Example:
import { create } from 'zustand' import { redux } from 'zustand/middleware' const reducer = (state, action) => { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 } case 'DECREMENT': return { ...state, count: state.count - 1 } default: return state } } const useStore = create( redux(reducer, { count: 0 }) // Initial state )
subscribeWithSelector
:
6. Purpose: Extends the store with a
subscribe
method that allows you to listen for specific state changes using selectors.Example:
import { create } from 'zustand' import { subscribeWithSelector } from 'zustand/middleware' const useStore = create( subscribeWithSelector((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), })) ) // Subscribe to a specific value change const unsub = useStore.subscribe( (state) => state.count, (count) => { console.log('Count changed to:', count) } )
logger
:
7. Purpose: Logs state changes to the console for debugging purposes. This is not an official middleware but can be easily implemented.
Example:
import { create } from 'zustand' const logger = (config) => (set, get, api) => config( (args) => { console.log('Applying:', args) set(args) console.log('New state:', get()) }, get, api ) const useStore = create( logger((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), })) )
Middlewares Summary:
persist
: Persist and rehydrate state.devtools
: Debugging with Redux DevTools.combine
: Combine state and actions.immer
: Immutable state handling.redux
: Redux-style reducers.subscribeWithSelector
: Enhanced subscriptions.logger
: Logs state changes (custom implementation).
Each middleware extends Zustand's functionality to make it more powerful and suitable for different use cases. Choose the middlewares that fit your project's needs to create a more flexible and maintainable state management solution.
Summary:
Zustand is simple yet powerful for global state management in React. By creating a store with state and actions, you can easily share state across components without the need for a context provider or prop drilling. Its lightweight nature and flexible API make it an excellent choice for many React projects.