zustand:基于hooks的react状态管理

react的状态管理

状态(State)是 React 中用于存储组件数据的特殊对象,它可以影响组件的渲染输出。状态管理的核心目标是确保数据的一致性、可预测性以及组件之间的数据流。

每个 React 组件都可以拥有自己的状态。在类组件中,状态通过 this.state 来管理,而在函数组件中,可以使用 useState Hook 来添加本地状态。

props(属性)是组件之间的数据传递机制。父组件可以通过 props 将其状态向下传递给子组件。但是,props 是单向数据流,子组件不能直接修改通过 props 接收的数据。当多个组件需要共享相同的状态时,可以将状态提升到它们最近的共同父组件中。这样,所有需要这个状态的组件都可以通过 props 访问它。

Context API 允许你无需明确通过组件树逐层传递 props,就能在组件树中共享数据。它适用于那些需要在应用程序的多个层级中访问的数据,如用户认证状态、主题设置等。

对于更复杂的应用程序,可能需要更强大的状态管理解决方案。这就是外部状态管理库发挥作用的地方,如 Redux、MobX、Zustand 等。这些库提供了更丰富的功能,如时间旅行调试、持久化、中间件支持等。

状态管理需要解决哪些关键问题?

  1. 数据一致性与可预测性:确保应用中的数据在任何时候都是一致的,并且状态变化是可预测的。

  2. 组件间通信:提供组件之间,特别是非直接父子关系的组件间共享和传递状态的机制。

  3. 可维护性与可扩展性:随着应用规模的增长,状态管理应保持代码的可维护性,并能够适应更复杂的数据流。

  4. 性能优化:减少不必要的组件重渲染,确保状态更新能够高效地触发所需的组件更新。

  5. 调试与错误处理:提供调试工具和错误处理机制,帮助开发者理解和维护状态变化,同时处理状态更新中的错误。

  6. 持久化与安全性:支持数据的持久化存储,并确保状态管理过程中的安全性,防止未授权访问。

外部状态管理库

随着 React 生态系统的不断发展,新的状态管理库不断涌现,以满足新的开发需求和解决现有库的不足。不同的项目根据其规模和复杂度需要不同级别的状态管理。小型项目可能只需要简单的状态管理,而大型项目则需要更复杂、更健壮的解决方案。

在React项目中,常用的外部状态管理库有:

  1. Redux:这是一个集中式的状态管理库,它通过单个store来存储整个应用的状态。Redux强调状态的不可变性和数据流的单向性,适用于大型应用和需要高度可预测状态变化的场景。

  2. MobX:MobX是一个简单且可扩展的状态管理库,采用观察者模式来实现状态管理。它提供了更简单直观的API,支持装饰器语法,易于学习且灵活,适合中小型项目。

  3. Zustand:Zustand是一个轻量级的状态管理库,它提供了简单的方式来管理React应用程序中的状态。它的主要特点是易于使用和轻量级,可以用于中心化或非中心化的数据流。

  4. Recoil:Recoil是Facebook开发的一个状态管理库,专为React设计,以解决复杂状态管理的痛点。它使用原子和选择器来管理状态,支持并行和异步操作。

  5. Jotai:Jotai是一个基于原子化的轻量级状态管理库,它借鉴了Redux和Recoil的设计。Jotai简单易用、无需过度配置、支持即时更新,适合小型到中型项目。

我们可以通过npmtrends查看这些库的下载量对比

npmtrends各状态管理库下载量对比

总体来看,Redux 仍然是最流行的状态管理库,尽管它的安装包大小较大。Zustand 作为一个较新的库,正在获得越来越多的关注,其轻量级的特点可能是吸引开发者的一个因素。MobX 和 Jotai 也有稳定的用户基础,但它们的下载量和社区活跃度低于 Redux 和 Zustand。

Zustand

Zustand 是一个轻量级且易于使用的状态管理库,特别适用于 React 应用程序。它的优势包括:

  1. 轻量级:Zustand 的整个代码库非常小巧,gzip 压缩后仅有 1KB,对项目性能影响极小。
  2. 简洁的 API:提供了简洁明了的 API,能够快速上手并使用它来管理项目状态。
  3. 基于钩子:Zustand 使用 React 的钩子机制作为状态管理的基础,与函数式组件和钩子的编程模型紧密配合,使得状态管理变得非常自然和无缝。
  4. 易于集成:可以轻松地与其他 React 库(如 Redux、MobX 等)共存,方便逐步迁移项目状态管理。
  5. 支持 TypeScript:Zustand 支持 TypeScript,让项目更具健壮性。
  6. 灵活性:允许根据项目需求自由组织状态树,适应不同的项目结构。
  7. 可拓展性:提供了中间件 (middleware) 的概念,允许你通过插件的方式扩展其功能,如日志记录、持久化存储、异步操作等。

然而,Zustand 也存在一些局限性:

  1. 社区规模:与 Redux 或 MobX 等更成熟的库相比,Zustand 的社区规模较小,这可能意味着在寻求帮助或查找资源时会遇到一些挑战。
  2. 学习资源:虽然 Zustand 的文档和示例相对齐全,但可能不如其他流行库那样丰富。
  3. 特定功能支持: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 状态和两个用于改变该状态的动作 incrementdecrement

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 路径中导入的一个工具函数,它对返回的对象进行浅层比较,如果对象的顶层属性和之前的状态一样,即使引用不同,也不会重新渲染组件。

  1. 选择性订阅:只订阅组件需要的状态片段,减少不必要的渲染。
  2. 浅比较:使用 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
    },
  ),
);

 

posted @ 2024-11-24 20:09  Yang9710  阅读(102)  评论(0编辑  收藏  举报