admin管理员组

文章数量:1443654

Zustand 用法记录

Zustand 可以做什么?

  1. 全局单例
  2. 组件间数据共选
  3. 替代 useState

安装

代码语言:bash复制
npm install zustand

npm install --save-dev typescript @types/react

基本用法

示例:创建一个共享的计数器。

目录结构

代码语言:plain复制
src/

├── components/

│   └── CounterComponent.tsx

├── stores/

│   └── useCounterStore.ts

└── index.tsx
代码语言:ts复制
// store.ts

import create from 'zustand';

// 定义状态的类型

interface CounterState {

    counter: number;

    increment: () => void;

    decrement: () => void;

    data: null | any;

    isLoading: boolean;

    fetchData: () => Promise<void>;

}

// 创建状态存储

const useCounterStore = create<CounterState>((set) => ({

    counter: 0,

    increment: () => set((state) => ({ counter: state.counter + 1 })),

    decrement: () => set((state) => ({ counter: state.counter - 1 })),

    data: null,

    isLoading: false,

    fetchData: async () => {

        set({ isLoading: true });

        try {

            const response = await fetch('');

            const result = await response.json();

            set({ data: result, isLoading: false });

        } catch (error) {

            set({ isLoading: false });

            console.error('Error fetching data:', error);

        }

    },

}));

export default useCounterStore;
代码语言:ts复制
// CounterComponent.tsx

import React from 'react';

import useCounterStore from './useCounterStore';

const CounterComponent: React.FC = () => {

    const counter = useCounterStore((state) => state.counter);

    const increment = useCounterStore((state) => state.increment);

    const decrement = useCounterStore((state) => state.decrement);

    const data = useCounterStore((state) => state.data);

    const isLoading = useCounterStore((state) => state.isLoading);

    const fetchData = useCounterStore((state) => state.fetchData);

    return (

        <div>

            <h1>计数器: {counter}</h1>

            <button onClick={increment}>增加</button>

            <button onClick={decrement}>减少</button>

            <hr />

            {isLoading ? (

                <p>正在加载...</p>

            ) : data ? (

                <pre>{JSON.stringify(data, null, 2)}</pre>

            ) : (

                <button onClick={fetchData}>获取数据</button>

            )}

        </div>

    );

};

export default CounterComponent;

如何更新状态?

借助 set 方法,实现状态更新。set 对象可以接收对象或者方法。

使用对象更新状态

代码语言:ts复制
import create from 'zustand';

// 定义状态类型

interface BearState {

  bears: number;

  increasePopulation: () => void;

  removeAllBears: () => void;

}

// 创建状态存储

const useBearStore = create<BearState>((set) => ({

  bears: 0,

  // 增加熊的数量

  increasePopulation: () => set({ bears: (state) => state.bears + 1 }),

  // 移除所有熊

  removeAllBears: () => set({ bears: 0 }),

}));

export default useBearStore;

使用函数更新状态

代码语言:ts复制
import create from 'zustand';

interface CounterState {

  counter: number;

  increment: () => void;

  decrement: () => void;

}

const useCounterStore = create<CounterState>((set) => ({

  counter: 0,

  // 增加计数器的值

  increment: () => set((state) => ({ counter: state.counter + 1 })),

  // 减少计数器的值

  decrement: () => set((state) => ({ counter: state.counter - 1 })),

}));

export default useCounterStore;

异步状态更新

适合数据请求等不得不异步的操作,比如数据初始化

代码语言:ts复制
import create from 'zustand';

interface DataState {

  data: null | any;

  isLoading: boolean;

  error: null | string;

  fetchData: () => Promise<void>;

}

const useDataStore = create<DataState>((set) => ({

  data: null,

  isLoading: false,

  error: null,

  // 异步获取数据

  fetchData: async () => {

    set({ isLoading: true, error: null });

    try {

      const response = await fetch('');

      if (!response.ok) {

        throw new Error('Network response was not ok');

      }

      const result = await response.json();

      set({ data: result, isLoading: false });

    } catch (err) {

      set({ error: err.message, isLoading: false });

    }

  },

}));

export default useDataStore;

批量更新状态

代码语言:ts复制
import create from 'zustand';

interface UserState {

  name: string;

  age: number;

  updateUser: (newName: string, newAge: number) => void;

}

const useUserStore = create<UserState>((set) => ({

  name: 'John',

  age: 30,

  // 批量更新用户信息

  updateUser: (newName, newAge) => set({ name: newName, age: newAge }),

}));

export default useUserStore;

如何状态很多,Store变得过于复杂怎么办?

  1. 拆分状态
  2. 浅比较和选择性订阅
  3. 使用 immer 进行不可变更新
  4. 异步操作优化
  5. 模块化和可复用性
  6. 拆分状态

将复杂对象拆分成多个小的状态存储,避免单个存储变得过于庞大。例如,若有一个用户对象包含基本信息、偏好设置和订单信息,可将这些信息拆分成不同的存储。

代码语言:ts复制
import create from 'zustand';

// 用户基本信息存储

interface UserBasicInfo {

    name: string;

    age: number;

    updateName: (newName: string) => void;

    updateAge: (newAge: number) => void;

}

const useUserBasicInfoStore = create<UserBasicInfo>((set) => ({

    name: 'John',

    age: 30,

    updateName: (newName) => set({ name: newName }),

    updateAge: (newAge) => set({ age: newAge }),

}));

// 用户偏好设置存储

interface UserPreferences {

    theme: string;

    language: string;

    updateTheme: (newTheme: string) => void;

    updateLanguage: (newLanguage: string) => void;

}

const useUserPreferencesStore = create<UserPreferences>((set) => ({

    theme: 'light',

    language: 'en',

    updateTheme: (newTheme) => set({ theme: newTheme }),

    updateLanguage: (newLanguage) => set({ language: newLanguage }),

}));

export { useUserBasicInfoStore, useUserPreferencesStore };
  1. 浅比较和选择性订阅

Zustand 默认使用浅比较来判断状态是否改变,只有状态发生改变时,订阅该状态的组件才会重新渲染。同时,你可以选择性地订阅状态的部分属性,避免不必要的重新渲染。

代码语言:ts复制
import React from 'react';

import { useUserBasicInfoStore } from './stores';

const UserNameComponent: React.FC = () => {

    // 仅订阅 name 属性

    const name = useUserBasicInfoStore((state) => state.name);

    return (

        <div>

            <p>User Name: {name}</p>

        </div>

    );

};

export default UserNameComponent;
  1. 使用 immer 进行不可变更新

处理复杂对象的嵌套更新时,使用 immer 库可以让代码更简洁,同时确保状态更新的不可变性。

代码语言:bash复制
npm install immer
代码语言:ts复制
import create from 'zustand';

import produce from 'immer';

interface NestedState {

    nested: {

        prop1: string;

        prop2: number;

    };

    updateNestedProp1: (newValue: string) => void;

}

const useNestedStateStore = create<NestedState>((set) => ({

    nested: {

        prop1: 'value1',

        prop2: 10,

    },

    updateNestedProp1: (newValue) =>

        set(

            produce((state) => {

                state.nested.prop1 = newValue;

            })

        ),

}));

export default useNestedStateStore;
  1. 异步操作优化

对于复杂对象的异步更新,可采用中间件或自定义逻辑来优化。例如,在请求数据时,可使用防抖或节流来减少不必要的请求。

代码语言:ts复制
import create from 'zustand';

import { debounce } from 'lodash';

interface AsyncDataState {

    data: any;

    isLoading: boolean;

    error: null | string;

    fetchData: (query: string) => void;

}

const useAsyncDataStore = create<AsyncDataState>((set) => {

    const fetchDataDebounced = debounce(async (query) => {

        set({ isLoading: true, error: null });

        try {

            const response = await fetch(`=${query}`);

            if (!response.ok) {

                throw new Error('Network response was not ok');

            }

            const result = await response.json();

            set({ data: result, isLoading: false });

        } catch (err) {

            set({ error: err.message, isLoading: false });

        }

    }, 300);

    return {

        data: null,

        isLoading: false,

        error: null,

        fetchData: (query) => fetchDataDebounced(query),

    };

});

export default useAsyncDataStore;
  1. 模块化和可复用性

将状态管理逻辑封装成可复用的模块,方便在不同组件中使用。例如,创建一个通用的列表管理模块。

代码语言:ts复制
import create from 'zustand';

interface ListState<T> {

    items: T[];

    addItem: (item: T) => void;

    removeItem: (index: number) => void;

}

const createListStore = <T>() =>

    create<ListState<T>>((set) => ({

        items: [],

        addItem: (item) => set((state) => ({ items: [...state.items, item] })),

        removeItem: (index) =>

            set((state) => ({

                items: state.items.filter((\_, i) => i !== index),

            })),

    }));

// 创建一个存储字符串列表的状态

const useStringListStore = createListStore<string>();

export { useStringListStore };

通过上述优化策略,可以更高效地使用 Zustand 管理复杂对象,提升应用的性能和可维护性。

用 Selector 优化性能

Selector 用来从复杂状态中,挑出你需要的部分。

简单示例:selectTotalPrice 函数根据 items 数组计算购物车的总价格,组件只需要订阅这个派生状态即可。

代码语言:ts复制
import create from 'zustand';

interface ShoppingCartState {

    items: { id: number; name: string; price: number; quantity: number }[];

    addItem: (item: { name: string; price: number; quantity: number }) => void;

}

const useShoppingCartStore = create<ShoppingCartState>((set) => ({

    items: [],

    addItem: (item) =>

        set((state) => ({

            items: [...state.items, { ...item, id: Date.now() }],

        })),

}));

const selectTotalPrice = (state: ShoppingCartState) =>

    state.items.reduce((total, item) => total + item.price \* item.quantity, 0);

const ShoppingCartSummary = () => {

    const totalPrice = useShoppingCartStore(selectTotalPrice);

    return <p>Total Price: ${totalPrice}</p>;

};

export default ShoppingCartSummary;

本文标签: Zustand 用法记录