Skip to content
 

状态订阅

更新: 9/4/2025字数: 0 字 时长: 0 分钟

Zustand subscribe,可以订阅一个状态,当状态变化时,会触发回调函数。

只要store state 发生变化,就会触发回调函数,另外就是这个订阅可以在组件内部订阅,也可以在组件外部订阅,如果在组件内部订阅需要放到useEffect中,防止重复订阅。

一、组件外部订阅

tsx
import React from "react";
import useBearStore from "../../../store/zustand";
// 组建外订阅
useBearStore.subscribe((state) => {
  // 每次点击 【更改年龄】 都会触发这个函数
  console.log(state.person);
});
function ZustandPage() {
  const { person } = useBearStore();
  return (
    <div>
      <div>
        <h1>Zustand 正常使用</h1>
        <p>bears: {person.name}</p>
        <p>bears: {person.age}</p>
        <button onClick={() => useBearStore.getState().updateName("张三")}>更改姓名</button>
        <button onClick={() => useBearStore.getState().updateAge(18)}>更改年龄</button>
        <br></br>
        <br></br>
      </div>
    </div>
  );
}
export default ZustandPage;

二、组件内部订阅

tsx
"use client";
import React from "react";
import useBearStore from "../../../store/zustand";
function ZustandPage() {
  const { person } = useBearStore();
  // 组件内订阅 person 状态
  React.useEffect(() => {
    useBearStore.subscribe((state) => {
      console.log("person:", state.person);
    });
  }, []);

  return (
    <div>
      <div>
        <h1>Zustand 正常使用</h1>
        <p>bears: {person.name}</p>
        <p>bears: {person.age}</p>
        <button onClick={() => useBearStore.getState().updateName("张三")}>更改姓名</button>
        <button onClick={() => useBearStore.getState().updateAge(18)}>更改年龄</button>
        <br></br>
        <br></br>
      </div>
    </div>
  );
}
export default ZustandPage;

三、subscribeWithSelector 优化

目前的订阅只要是store内部任意的state发生变化,都会触发回调函数,我们希望只订阅 age 的变化,可以使用中间件subscribeWithSelector 订阅单个状态。

store

ts
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { subscribeWithSelector } from "zustand/middleware";

interface ObjState {
  person: { name: string; age: number; sex: string };
  updateName: (name: string) => void;
  updateAge: () => void;
  reset: () => void;
}

// 使用immer和subscribeWithSelector
export const useBearStore = create<ObjState>()(
  subscribeWithSelector(
    immer((set, get) => ({
      person: {
        name: "xinjie",
        age: 18,
        sex: "male"
      },
      updateName: (name) =>
        set((state) => {
          state.person.name = name;
        }),
      updateAge: () =>
        set((state) => {
          state.person.age++;
        }),
      reset: () =>
        set({
          person: {
            name: "xinjie",
            age: 18,
            sex: "male"
          }
        })
    }))
  )
);

export default useBearStore;

3.1、订阅整个 store 变化

ts
const unsubscribe = useBearStore.subscribe(
  (state) => state,
  (state) => {
    console.log("整个store变化:", state);
  }
);

// 组件卸载时取消订阅
useEffect(() => {
  return () => unsubscribe();
}, []);

3.2、选择性订阅特定状态

ts
// 只监听person.age的变化
const unsubscribeAge = useBearStore.subscribe(
  (state) => state.person.age,
  (age) => {
    console.log("年龄变化:", age);
  },
  {
    fireImmediately: true, // 是否立即触发一次回调
    equalityFn: (a, b) => a === b // 自定义相等比较函数
  }
);

3.3、选择性订阅特定状态

tsx
import { useEffect } from "react";

function PersonInfo() {
  const { person, updateAge } = useBearStore();

  useEffect(() => {
    const unsubscribe = useBearStore.subscribe(
      (state) => state.person.name,
      (name) => {
        console.log("名字变化:", name);
      }
    );

    return unsubscribe;
  }, []);

  return (
    <div>
      <p>Name: {person.name}</p>
      <p>Age: {person.age}</p>
      <button onClick={updateAge}>Increase Age</button>
    </div>
  );
}

减少不必要的渲染和回调和 状态监听更精确(subscribeWithSelector)。

我见青山多妩媚,料青山见我应如是。