zustand
是一个用于React应用的简单、快速且零依赖的状态管理库。它使用简单的钩子(hooks)API来创建全局状态,使得在组件之间共享状态变得容易。
React学习Day10
基本用法
-
安装:首先,你需要安装
zustand
库。npm install zustand
-
创建一个状态存储:使用
createStore
函数来创建一个新的状态存储。 -
设置初始状态:你可以提供一个对象作为初始状态,对象的每个属性都将成为状态的一部分。
-
定义状态更新函数:这些函数可以修改状态存储中的状态。
-
使用状态:在组件中使用
useStore
钩子来访问和更新状态。
代码示例
以下是一个使用zustand
的基本示例:
store / user.tsx 中创建一个状态
import { create } from "zustand";
interface Store {
count: number;
inc: (num: number) => void;
}
// 这里的 <Store> 表示 create 函数接受一个泛型参数,这个参数是 Store 接口的类型。
// 相当于在调用函数的时候指定type参数,类似 fun1<number>(1);
const createUserStore = create<Store>((set) => {
return {
count: 0,
inc: (num: number) => {
set((state) => ({ count: state.count + num }));
},
};
});
export default createUserStore;
在组件中使用这个状态
component / Son.tsx
import React from "react";
import createUserStore from "../store/user"; // 引入
interface SonProps {
data: number;
changeFatherMoney: (data: number) => void;
}
const Son: React.FC<SonProps> = ({ data, changeFatherMoney }) => {
const { count, inc } = createUserStore(); // 导出
return (
<div style={{ border: "1px red solid" }}>
<div>用户信息counter:{count}</div>
<button
onClick={() => {
inc(10);
}}
>
+10
</button>
<p>这里是子组件</p>
父亲的数据是:{data}
<button onClick={() => changeFatherMoney(data - 1)}>用父亲一块钱</button>
</div>
);
};
export default Son
泛型介绍
在TypeScript中,<Store>
是一个泛型参数的表示方式。这里的 <Store>
表示 create
函数接受一个泛型参数,这个参数是 Store
接口的类型。
泛型在TypeScript中是一种强大的方式,允许你为函数、接口或类定义类型参数,这些参数在定义时不必指定具体的类型,而是在使用时指定。这样做可以提高代码的复用性和灵活性。
在你给出的示例中:
interface Store {
count: number;
inc: (num: number) => void;
}
const createUserStore = create<Store>((set) => {
return {
count: 0,
inc: (num: number) => {
set((state) => ({ count: state.count + num }));
},
};
});
这里的<Store>
泛型,规定了create的返回值类型,返回的状态存储对象的类型,而不是用来规定参数类型。
create 表示 create 函数接受一个泛型参数 Store。这个泛型参数是类型接口 Store,它定义了 store 的形状,即包含 count 和 inc 属性。
在这里不是对泛型参数进行约束,而是直接指定了 create 函数所使用的泛型类型,规定了返回的对象必须是否符合 Store 接口的定义
interface Store
定义了一个接口,其中包含一个count
属性和一个inc
方法。create<Store>
是zustand
的create
函数,它被调用时使用了泛型参数<Store>
。这意味着create
函数将创建一个状态存储,其形状(state shape)将由Store
接口定义。createUserStore
是一个函数,当调用时会返回一个符合Store
接口的状态存储实例。
为什么在 Zustand 中使用泛型而不是接口
在 Zustand 的 create
函数中,泛型不仅用于指定返回值的类型,还用于定义函数内部 set
方法的参数类型等,这比单独使用接口来定义返回值类型更具优势。
示例代码
import { create } from "zustand";
interface Store {
count: number;
inc: (num: number) => void;
}
const createUserStore = create<Store>((set) => {
return {
count: 0,
inc: (num: number) => {
set((state) => ({ count: state.count + num }));
},
};
});
解释
-
泛型的作用:
create<Store>
中的<Store>
泛型不仅指定了createUserStore
的返回值类型,也规定了回调函数参数set
的类型。set
函数的类型定义需要知道Store
的结构,以便 TypeScript 可以正确推断set
中state
的类型。
-
接口的局限性:
- 如果只使用接口来定义返回值类型,会失去对
set
函数类型的类型推断和约束。 - 接口无法单独定义
create
函数返回的回调函数set
的参数类型。
- 如果只使用接口来定义返回值类型,会失去对
示例:如果只用接口定义返回值类型
如果只用接口定义返回值类型,无法涵盖 set
函数类型的约束:
const createUserStore = create((set: (fn: (state: Store) => Store) => void) => {
return {
count: 0,
inc: (num: number) => {
set((state) => ({ count: state.count + num }));
},
};
} as Store);
这种方式缺乏泛型提供的灵活性和类型推断能力,代码变得不那么优雅,类型定义也不那么清晰。
总结
- 泛型用途广泛:泛型不仅可以规定返回值类型,还可以用于函数参数、类属性和方法、接口属性和方法、类型别名等。
- Zustand 中使用泛型的优势:在 Zustand 的
create
函数中使用泛型,不仅规定了返回值类型,还涵盖了内部回调函数set
的参数类型约束,使代码更加类型安全和简洁。 - 接口的局限性:仅使用接口来定义返回值类型,无法对函数参数类型进行全面约束,失去了泛型提供的类型推断能力。
通过使用泛型,你可以使代码更具通用性、灵活性和类型安全性,特别是在处理复杂的类型结构时。
调试工具
npm i simple-zustand-devtools -D
import create from 'zustand'
// 导入核心方法
import { mountStoreDevtool } from 'simple-zustand-devtools'
// 省略部分代码...
// 开发环境开启调试
if (process.env.NODE_ENV === 'development') {
mountStoreDevtool('channelStore', useChannelStore)
}
export default useChannelStore
注意事项
zustand
的状态存储是单一的,意味着所有组件共享相同的状态副本。- 状态更新是响应式的,组件会在状态变化时重新渲染。
- 状态存储的创建应该是一次性的,通常在单独的文件中进行,然后被多个组件导入和使用。
zustand
是一个轻量级的状态管理解决方案,特别适合中小型项目或者需要快速设置全局状态的场景。