Skip to content

Zustand 状态管理

代码极少;不需要 Provider;简洁度高;性能好;轻量,灵活,适合中小型到大型项目

安装

bash
npm install zustand
npm install zustand

创建一个 Store

js
// src/stores/counterStore.js
import { create } from "zustand";

// create() 函数接收一个回调,回调的参数是 set 函数
// set 函数是唯一用来修改状态的工具
const useCounterStore = create((set) => ({
  // 1. 定义 State
  count: 0,
  posts: [],
  loading: false,

  // 2. 定义 "Actions" (它们就是普通的方法)
  increment: () => {
    // 调用 set 函数来更新状态
    set((state) => ({ count: state.count + 1 }));
  },

  decrement: () => {
    set((state) => ({ count: state.count - 1 }));
  },

  incrementByAmount: (amount) => {
    set((state) => ({ count: state.count + amount }));
  },

  // 3. 定义异步 Action
  fetchPosts: async () => {
    set({ loading: true }); // 进入加载状态
    try {
      const response = await fetch(
        "https://jsonplaceholder.typicode.com/posts"
      );
      const data = await response.json();
      set({ posts: data.slice(0, 5), loading: false }); // 更新数据,结束加载
    } catch (error) {
      console.error("获取数据失败", error);
      set({ loading: false }); // 结束加载
    }
  },
}));

export default useCounterStore;
// src/stores/counterStore.js
import { create } from "zustand";

// create() 函数接收一个回调,回调的参数是 set 函数
// set 函数是唯一用来修改状态的工具
const useCounterStore = create((set) => ({
  // 1. 定义 State
  count: 0,
  posts: [],
  loading: false,

  // 2. 定义 "Actions" (它们就是普通的方法)
  increment: () => {
    // 调用 set 函数来更新状态
    set((state) => ({ count: state.count + 1 }));
  },

  decrement: () => {
    set((state) => ({ count: state.count - 1 }));
  },

  incrementByAmount: (amount) => {
    set((state) => ({ count: state.count + amount }));
  },

  // 3. 定义异步 Action
  fetchPosts: async () => {
    set({ loading: true }); // 进入加载状态
    try {
      const response = await fetch(
        "https://jsonplaceholder.typicode.com/posts"
      );
      const data = await response.json();
      set({ posts: data.slice(0, 5), loading: false }); // 更新数据,结束加载
    } catch (error) {
      console.error("获取数据失败", error);
      set({ loading: false }); // 结束加载
    }
  },
}));

export default useCounterStore;
  • create 函数创建了一个 Hook (useCounterStore)。
  • set 函数是核心。当你调用它时,Zustand 会智能地将你传入的对象合并到现有状态中(shallow merge),类似于 this.setState。
  • 你可以在 store 中直接定义异步函数,并在其中多次调用 set 来更新不同阶段的状态(例如,开始加载 -> 加载完成)。

在组件中使用 Store

js
// src/components/Counter.js
import React, { useEffect } from "react";
import useCounterStore from "../stores/counterStore";

function Counter() {
  // --- 方法一:推荐!使用选择器(selector)来获取你需要的特定状态 ---
  // 这样做的好处是,只有当 `count` 这个值变化时,组件才会重新渲染。
  const count = useCounterStore((state) => state.count);
  const { increment, decrement, incrementByAmount } = useCounterStore();

  // --- 方法二:获取整个 store (当 store 中任何数据变化时都会重渲染) ---
  // const { count, increment, decrement, incrementByAmount } = useCounterStore();

  return (
    <div>
      <h2>计数器 (Zustand)</h2>
      <span>{count}</span>
      <div>
        <button onClick={increment}>增加</button>
        <button onClick={decrement}>减少</button>
        <button onClick={() => incrementByAmount(5)}>增加 5</button>
      </div>
    </div>
  );
}

function PostList() {
  const { posts, loading, fetchPosts } = useCounterStore();

  // 组件挂载时自动获取数据
  useEffect(() => {
    fetchPosts();
  }, [fetchPosts]); // fetchPosts 函数引用是稳定的,所以这只运行一次

  if (loading) {
    return <p>正在加载文章...</p>;
  }

  return (
    <div>
      <h2>文章列表 (Zustand)</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

// 在 App.js 中组合起来
export default function App() {
  return (
    <div>
      <Counter />
      <hr />
      <PostList />
    </div>
  );
}
// src/components/Counter.js
import React, { useEffect } from "react";
import useCounterStore from "../stores/counterStore";

function Counter() {
  // --- 方法一:推荐!使用选择器(selector)来获取你需要的特定状态 ---
  // 这样做的好处是,只有当 `count` 这个值变化时,组件才会重新渲染。
  const count = useCounterStore((state) => state.count);
  const { increment, decrement, incrementByAmount } = useCounterStore();

  // --- 方法二:获取整个 store (当 store 中任何数据变化时都会重渲染) ---
  // const { count, increment, decrement, incrementByAmount } = useCounterStore();

  return (
    <div>
      <h2>计数器 (Zustand)</h2>
      <span>{count}</span>
      <div>
        <button onClick={increment}>增加</button>
        <button onClick={decrement}>减少</button>
        <button onClick={() => incrementByAmount(5)}>增加 5</button>
      </div>
    </div>
  );
}

function PostList() {
  const { posts, loading, fetchPosts } = useCounterStore();

  // 组件挂载时自动获取数据
  useEffect(() => {
    fetchPosts();
  }, [fetchPosts]); // fetchPosts 函数引用是稳定的,所以这只运行一次

  if (loading) {
    return <p>正在加载文章...</p>;
  }

  return (
    <div>
      <h2>文章列表 (Zustand)</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

// 在 App.js 中组合起来
export default function App() {
  return (
    <div>
      <Counter />
      <hr />
      <PostList />
    </div>
  );
}

程序员小洛文档