1. 基本用法
Zustand 的核心是通过 create 函数创建一个状态管理器(store),并使用 useStore Hook 在组件中访问和更新状态。
创建状态管理器
import create from 'zustand';
const useStore = create((set) => ({
count: 0, // 定义状态
increment: () => set((state) => ({ count: state.count + 1 })), // 更新状态的方法
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
在组件中使用状态
import React from 'react';
import { useStore } from './store';
function Counter() {
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;
2. 高级特性
2.1 中间件
Zustand 支持中间件,用于扩展功能,例如持久化状态或集成开发工具。
-
持久化状态
import create from 'zustand'; import { persist } from 'zustand/middleware'; const useStore = create( persist( (set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), }), { name: 'my-store' } // 持久化存储的名称 ) ); -
集成 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 })), }), { name: 'my-store' } // 可选配置 ) );
2.2 异步操作
Zustand 支持在状态管理器中处理异步逻辑。
const useAsyncStore = create((set) => ({
data: null,
loading: false,
fetchData: async () => {
set({ loading: true }); // 设置加载状态
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
set({ data: result, loading: false }); // 更新数据
} catch (error) {
set({ loading: false });
console.error(error);
}
},
}));
2.3 选择性订阅
Zustand 支持选择性订阅,只监听需要的状态片段,减少不必要的组件重渲染。
const useStore = create((set) => ({
user: { name: '', age: 0 },
updateUser: (user) => set({ user }),
}));
function UserProfile() {
const name = useStore((state) => state.user.name); // 只监听 name 属性
const updateName = useStore((state) => state.updateUser);
return (
<div>
<p>Name: {name}</p>
<input
type="text"
value={name}
onChange={(e) => updateName({ name: e.target.value })}
/>
</div>
);
}
3. 实际项目中的应用
示例:待办事项应用
以下是一个完整的待办事项应用示例,展示如何在项目中使用 Zustand 管理状态。
创建待办事项 Store
import create from 'zustand';
const useTodoStore = create((set) => ({
todos: [],
addTodo: (todo) => set((state) => ({ todos: [...state.todos, todo] })),
removeTodo: (index) => set((state) => {
const todos = [...state.todos];
todos.splice(index, 1);
return { todos };
}),
}));
使用待办事项 Store
import React, { useState } from 'react';
import useTodoStore from './todoStore';
const TodoApp = () => {
const { todos, addTodo, removeTodo } = useTodoStore();
const [inputValue, setInputValue] = useState('');
const handleAddTodo = () => {
if (inputValue.trim()) {
addTodo(inputValue);
setInputValue('');
}
};
return (
<div>
<h1>Todo List</h1>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>
{todo}
<button onClick={() => removeTodo(index)}>Remove</button>
</li>
))}
</ul>
</div>
);
};
export default TodoApp;
4. 其他特性
4.1 TypeScript 支持
Zustand 提供了完整的 TypeScript 支持,确保类型安全。
import create, { StateCreator } from 'zustand';
interface StoreState {
count: number;
increment: () => void;
}
const createStore: StateCreator<StoreState> = (set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
});
const useStore = create<StoreState>(createStore);
Zustand 不仅适用于 React,还可以在其他框架(如 Svelte 或 Vue)中使用。
在使用 Zustand 时,直接修改 Store 的值并不是推荐的方式,因为 Zustand 的设计是基于不可变数据原则的。这意味着你需要通过定义好的 更新函数 来修改状态,而不是直接操作状态对象。
如果你需要将一个外部的数组(比如 useList)存放到 Zustand 的 Store 中,可以通过以下方式实现:
示例:将外部数组存入 Zustand Store
1. 定义 Store
假设你有一个 useList,你希望将其存储到 Zustand 的状态中。你需要在 Store 中定义一个状态字段(比如 list),并提供一个方法来更新这个字段。
import create from 'zustand';
const useStore = create((set) => ({
list: [], // 定义初始状态
setList: (newList) => set({ list: newList }), // 提供更新方法
}));
2. 在组件中使用并更新状态
假设你在某个组件中获取了 useList,并希望将其存入 Zustand 的 Store 中。
import React, { useEffect } from 'react';
import { useStore } from './store'; // 引入上面定义的 Store
const MyComponent = () => {
const { list, setList } = useStore(); // 从 Store 中获取状态和更新方法
const useList = ['item1', 'item2', 'item3']; // 假设这是你从外部获取的数组
// 在组件加载时将 useList 存入 Zustand 的 list 状态中
useEffect(() => {
setList(useList);
}, [setList]);
return (
<div>
<h1>Items from Zustand Store:</h1>
<ul>
{list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
};
export default MyComponent;
关键点说明
- 不可变数据原则:Zustand 遵循不可变数据原则,因此你不能直接修改状态对象,而是通过调用更新函数来替换状态。
- 更新方法:在 Store 中定义一个更新方法(如
setList),并通过这个方法将外部数组存入状态中。 - 副作用处理:在组件中使用
useEffect来处理状态更新,确保在组件加载或数据变化时触发更新。
如果需要动态更新数组
如果你需要动态更新数组(比如添加、删除或修改数组中的元素),可以在 Store 中定义更细粒度的更新方法。例如:
const useStore = create((set) => ({
list: [], // 初始状态
setList: (newList) => set({ list: newList }), // 设置整个数组
addItem: (item) => set((state) => ({ list: [...state.list, item] })), // 添加元素
removeItem: (index) => set((state) => {
const newList = [...state.list];
newList.splice(index, 1);
return { list: newList };
}),
}));
这样,你可以根据需要调用 addItem 或 removeItem 方法来动态更新数组。
总结
在 Zustand 中,永远不要直接修改状态对象,而是通过定义好的更新方法来操作状态。这种方式不仅符合不可变数据原则,还能确保状态更新的可预测性和组件的正确渲染。