React 项目中zustand状态管理最佳实践

4,867 阅读3分钟

Zustand是一个非常小巧但功能强大的状态管理库,可以帮助我们轻松地在React应用程序中管理状态。

基本例子

  1. 定义State类型

在定义状态时,我们需要定义状态对象的类型。我们可以使用TypeScript的类型系统来确保我们的状态对象的属性和值的类型是正确的。例如:

interface ICounterState {
  count: number;
  increment: () => void;
  decrement: () => void;
}
  1. 使用create方法创建store

我们可以使用Zustand的create方法创建store。在使用create方法时,我们需要将默认状态对象和操作状态对象的函数传递给它。例如:

import create from 'zustand';

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

在这个例子中,我们创建了一个名为useCounterStore的Zustand store,它具有count、increment和decrement属性。

  1. 使用useStore Hook访问store

我们可以使用useStore Hook访问store中的状态和方法。使用useStore Hook时,我们可以指定状态的类型,并使用对象解构来访问状态和方法。例如:

import { useStore } from './useCounterStore';

function Counter() {
  const { count, increment, decrement } = useStore((state) => ({
    count: state.count,
    increment: state.increment,
    decrement: state.decrement,
  }));

  return (
    <View>
      <Text>{count}</Text>
      <Button title="+" onPress={increment} />
      <Button title="-" onPress={decrement} />
    </View>
  );
}

在这个例子中,我们使用useStore Hook访问count、increment和decrement属性,并将它们渲染到视图中。

最佳实践

  1. 划分状态

将状态划分为多个store,每个store仅关注特定领域的状态。这将有助于减少store之间的耦合并简化应用程序的状态管理。例如,在一个电子商务应用程序中,我们可以将购物车状态划分为一个单独的store,将订单状态划分为另一个store。

  1. 使用immer做状态更新

我们可以使用Zustand的内置immer库来更新状态。immer允许我们在更新状态时使用可变性,但在最终返回状态时自动将其转换为不可变性。例如:

import create from 'zustand';
import produce from 'immer';

interface ITodo {
  id: number;
  text: string;
  completed: boolean;
}

interface ITodoState {
  todos: ITodo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: number) => void;
}

const useTodoStore = create<ITodoState>((set) => ({
  todos: [],
  addTodo: (text) =>
    set(
      produce((state) => {
        state.todos.push({ id: state.todos.length + 1, text, completed: false });
      })
    ),
  toggleTodo: (id) =>
    set(
      produce((state) => {
        const todo = state.todos.find((todo) => todo.id === id);
        if (todo) {
          todo.completed = !todo.completed;
        }
      })
    ),
}));

在这个例子中,我们使用immer来添加新的todo项和切换todo项的completed属性。

  1. 使用中间件

使用中间件来处理store中的副作用。中间件可以帮助我们在状态更改之前或之后执行一些操作。例如,我们可以使用中间件来将状态存储在本地存储中,或者在状态更改时记录日志。

const localStorageMiddleware = (config: StoreApi<State>) => (set: SetState<State>, get: GetState<State>, api: StoreApi<State>) =>
  (args: State | ((state: State) => State)) => {
    const nextState = typeof args === 'function' ? args(get()) : args;
    set(nextState);
    localStorage.setItem('appState', JSON.stringify(nextState));
  };

const useCounterStore = create<State>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}), { middleware: [localStorageMiddleware] });

在这个例子中,我们使用了一个中间件来将状态存储在本地存储中。

  1. 使用select函数

使用select函数来从store中选择部分状态。这将帮助我们避免在组件中访问整个store状态对象,从而提高性能。例如:

const useTodoStore = create((set) => ({
  todos: [],
  addTodo: (text: string) => set((state) => [...state.todos, { id: uuid(), text, completed: false }]),
  toggleTodo: (id: string) =>
    set((state) =>
      state.todos.map((todo) => {
        if (todo.id === id) {
          return { ...todo, completed: !todo.completed };
        }
        return todo;
      })
    ),
}));

const useCompletedTodos = () => {
  const todos = useTodoStore((state) => state.todos);
  return todos.filter((todo) => todo.completed);
};

在这个例子中,我们使用useTodoStore来访问整个store,然后使用select函数来选择已完成的todo项。