zustand:基于hooks的react状态管理
react的状态管理
状态(State)是 React 中用于存储组件数据的特殊对象,它可以影响组件的渲染输出。状态管理的核心目标是确保数据的一致性、可预测性以及组件之间的数据流。
每个 React 组件都可以拥有自己的状态。在类组件中,状态通过 this.state
来管理,而在函数组件中,可以使用 useState
Hook 来添加本地状态。
props(属性)是组件之间的数据传递机制。父组件可以通过 props 将其状态向下传递给子组件。但是,props 是单向数据流,子组件不能直接修改通过 props 接收的数据。当多个组件需要共享相同的状态时,可以将状态提升到它们最近的共同父组件中。这样,所有需要这个状态的组件都可以通过 props 访问它。
Context API 允许你无需明确通过组件树逐层传递 props,就能在组件树中共享数据。它适用于那些需要在应用程序的多个层级中访问的数据,如用户认证状态、主题设置等。
对于更复杂的应用程序,可能需要更强大的状态管理解决方案。这就是外部状态管理库发挥作用的地方,如 Redux、MobX、Zustand 等。这些库提供了更丰富的功能,如时间旅行调试、持久化、中间件支持等。
状态管理需要解决哪些关键问题?
-
数据一致性与可预测性:确保应用中的数据在任何时候都是一致的,并且状态变化是可预测的。
-
组件间通信:提供组件之间,特别是非直接父子关系的组件间共享和传递状态的机制。
-
可维护性与可扩展性:随着应用规模的增长,状态管理应保持代码的可维护性,并能够适应更复杂的数据流。
-
性能优化:减少不必要的组件重渲染,确保状态更新能够高效地触发所需的组件更新。
-
调试与错误处理:提供调试工具和错误处理机制,帮助开发者理解和维护状态变化,同时处理状态更新中的错误。
-
持久化与安全性:支持数据的持久化存储,并确保状态管理过程中的安全性,防止未授权访问。
外部状态管理库
随着 React 生态系统的不断发展,新的状态管理库不断涌现,以满足新的开发需求和解决现有库的不足。不同的项目根据其规模和复杂度需要不同级别的状态管理。小型项目可能只需要简单的状态管理,而大型项目则需要更复杂、更健壮的解决方案。
在React项目中,常用的外部状态管理库有:
-
Redux:这是一个集中式的状态管理库,它通过单个store来存储整个应用的状态。Redux强调状态的不可变性和数据流的单向性,适用于大型应用和需要高度可预测状态变化的场景。
-
MobX:MobX是一个简单且可扩展的状态管理库,采用观察者模式来实现状态管理。它提供了更简单直观的API,支持装饰器语法,易于学习且灵活,适合中小型项目。
-
Zustand:Zustand是一个轻量级的状态管理库,它提供了简单的方式来管理React应用程序中的状态。它的主要特点是易于使用和轻量级,可以用于中心化或非中心化的数据流。
-
Recoil:Recoil是Facebook开发的一个状态管理库,专为React设计,以解决复杂状态管理的痛点。它使用原子和选择器来管理状态,支持并行和异步操作。
-
Jotai:Jotai是一个基于原子化的轻量级状态管理库,它借鉴了Redux和Recoil的设计。Jotai简单易用、无需过度配置、支持即时更新,适合小型到中型项目。
我们可以通过npmtrends查看这些库的下载量对比
总体来看,Redux 仍然是最流行的状态管理库,尽管它的安装包大小较大。Zustand 作为一个较新的库,正在获得越来越多的关注,其轻量级的特点可能是吸引开发者的一个因素。MobX 和 Jotai 也有稳定的用户基础,但它们的下载量和社区活跃度低于 Redux 和 Zustand。
Zustand
Zustand 是一个轻量级且易于使用的状态管理库,特别适用于 React 应用程序。它的优势包括:
- 轻量级:Zustand 的整个代码库非常小巧,gzip 压缩后仅有 1KB,对项目性能影响极小。
- 简洁的 API:提供了简洁明了的 API,能够快速上手并使用它来管理项目状态。
- 基于钩子:Zustand 使用 React 的钩子机制作为状态管理的基础,与函数式组件和钩子的编程模型紧密配合,使得状态管理变得非常自然和无缝。
- 易于集成:可以轻松地与其他 React 库(如 Redux、MobX 等)共存,方便逐步迁移项目状态管理。
- 支持 TypeScript:Zustand 支持 TypeScript,让项目更具健壮性。
- 灵活性:允许根据项目需求自由组织状态树,适应不同的项目结构。
- 可拓展性:提供了中间件 (middleware) 的概念,允许你通过插件的方式扩展其功能,如日志记录、持久化存储、异步操作等。
然而,Zustand 也存在一些局限性:
- 社区规模:与 Redux 或 MobX 等更成熟的库相比,Zustand 的社区规模较小,这可能意味着在寻求帮助或查找资源时会遇到一些挑战。
- 学习资源:虽然 Zustand 的文档和示例相对齐全,但可能不如其他流行库那样丰富。
- 特定功能支持:Zustand 可能在某些特定功能或高级用例的支持上不如其他状态管理库全面。
总的来说,Zustand 提供了一种简单、直观且高效的状态管理方式,尤其适合那些希望避免 Redux 等库复杂性,同时需要一个易于上手和轻量级解决方案的开发者。
Zustand使用
安装
首先,你需要通过 npm 或 yarn 将 Zustand 添加到你的项目中:
npm install zustand
yarn add zustand
基础用法
1. 创建一个 Store
创建一个 store 来存储和管理状态。
import create from 'zustand';
const useStore = create(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
}));
export default useStore;
在这个示例中,我们创建了一个具有 count
状态和两个用于改变该状态的动作 increment
和 decrement
。
2. 在组件中使用 Store
在 React 组件中,你可以使用自定义 Hook useStore
来访问和修改状态。
import React from 'react';
import { useStore } from './store'; // 假设你的 store 文件名为 store.js
function Counter() {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
const decrement = useStore((state) => state.decrement);
const decrement = useStore((state) => state.decrement);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
export default Counter;
3. 异步操作
import create from 'zustand';
import { getCount } from '@/services'; // 模拟异步请求的接口
const useStore = create((set, get) => ({
count: 0,
params: {id: 1}, // 模拟请求的参数
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
getCount: async () => {
const res = await getCount(get().params); // 通过get获取当前store的状态
set({ count: res.data });
}
}));
export default useStore;
在其他组件中调用异步操作
import React, { useEffect } from 'react';
import { useStore } from './store'; // 假设你的 store 文件名为 store.js
function Counter() {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
const decrement = useStore((state) => state.decrement);
const decrement = useStore((state) => state.decrement);
const getCount = useStore((state) => state.getCount);
useEffect(()=>{
getCount();
}, [])
return (
<div>
<h1>Count: {count}</h1>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
export default Counter;
4. 获取多个状态
前文中我们的状态与方法都是单独一句声明的,为什么不直接获取整个状态库呢?
const state = useStore();
这样做可以获取整个 store,但请记住这样做会导致组件在每次状态变化时都重新渲染!
更优雅的实现方式:
import { useShallow } from 'zustand/react/shallow';
const { count, increment, decrement } = useStore(
useShallow((state) => ({
count: state.count,
increment: state.increment,
decrement: state.decrement,
}))
);
// const { count, increment, decrement } = useStore(); // 待验证
useShallow
是从 Zustand 提供的 /shallow
路径中导入的一个工具函数,它对返回的对象进行浅层比较,如果对象的顶层属性和之前的状态一样,即使引用不同,也不会重新渲染组件。
- 选择性订阅:只订阅组件需要的状态片段,减少不必要的渲染。
- 浅比较:使用
useShallow
进行浅比较,减少不必要的渲染。
5. 状态持久化
使用 middleware
来实现状态的持久化。
import create from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware'
const useStoreWithPersistence = create(
persist(
(set, get) => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
}),
{
name: 'my_zustand_store', // name of the item in the storage (must be unique)
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
},
),
);