likes
comments
collection
share

从观察者模式到 Zustand:探寻状态管理的极简之道本文将从状态管理的核心设计理念出发以及对观察者模式的简单实现,带领

作者站长头像
站长
· 阅读数 51

引言

本文将从状态管理的核心设计理念出发以及对观察者模式的简单实现,带领大家一步步探索 Zustand 的核心原理,并通过实际代码展示如何实现一个类似 Zustand 的状态管理库。 本文采用面向对象的方式,方便读者更容易理解其背后的设计思路和实现细节

目标

  1. 灵活应用观察者设计模式,解耦实项目中的数据流和逻辑
  2. 掌握 Zustand 的核心设计理念,了解其简洁高效的状态管理方法
  3. 通过 TypeScript 编写自定义的 Hooks,提升代码质量和类型安全性
  4. 熟练使用 TypeScript 的高级类型特性,优化状态管理逻辑
  5. 利用 useSyncExternalStore 优化状态管理的订阅与更新流程
  6. 学会使用usRef防止一些没必要的渲染
  7. 学会中间件开发思想

观察者设计模式

在很多前端框架或库中,观察者模式随处可见。Vue、MobX、Promise、React-query 等库都通过观察者模式实现了数据的订阅与更新。观察者模式的核心思想就是 发布-订阅,即观察者订阅主题对象,当主题发生变化时,通知所有观察者进行相应的操作

观察者模式的基本结构:

  • 主题(Subject):负责维护状态并通知观察者。
  • 观察者(Observer):订阅主题的变化并做出响应

从观察者模式到 Zustand:探寻状态管理的极简之道本文将从状态管理的核心设计理念出发以及对观察者模式的简单实现,带领

// 主题对象
class Subject<T> {
  private watchers: Set<Watcher<T>>;
  private state: T | null;

  constructor(state: T) {
    this.watchers = new Set();
    this.state = state || null;
  }

  // 注册观察者
  register(watcher: Watcher<T>) {
    this.watchers.add(watcher);
  }

  // 移除观察者
  remove(watcher: Watcher<T>) {
    this.watchers.delete(watcher);
  }

  // 通知所有观察者
  notify(newState: T, oldState: T) {
    this.watchers.forEach(watcher => {
      watcher.update(newState, oldState);
    });
  }

  // 更新数据
  update(newState: T) {
    const oldState = this.state;
    this.state = newState;
    this.notify(newState, oldState);
  }
}

// 观察者类
class Watcher<T> {
  private subscribe: (newVal: T, oldVal: T) => void;

  constructor(subscribe: (newVal: T, oldVal: T) => void) {
    this.subscribe = subscribe;
  }

  // 更新时执行回调
  update(newVal: T, oldVal: T) {
    if (newVal !== oldVal) {
      this.subscribe(newVal, oldVal);
    }
  }
}

从观察者模式到Zustand

Zustand 的状态管理可以看作是观察者模式的最佳实践。它通过 store 持有应用状态,并提供 subscribe 方法供组件订阅变化,从而将状态管理逻辑和框架层面逻辑进行分离, 为了更好地理解 Zustand 的实现原理,我们通过以下代码进行深度探索。

Zustand 整体逻辑架构图

从观察者模式到 Zustand:探寻状态管理的极简之道本文将从状态管理的核心设计理念出发以及对观察者模式的简单实现,带领

  • Store: 核心状态管理对象
  • Subject: 当状态改变时通知所有订阅者
  • Watcher: 观察并监听状态变化
  • useStore: React 组件与 Zustand 交互的桥梁
实现 Store

Store 是状态管理的核心,它持有应用的状态,并通过 setState 方法来更新状态。当状态发生变化时,所有注册的观察者都会收到通知,并执行相应的操作

  • state: 当前状态
  • getState: 获取当前状态
  • setState: 修改改变状态
  • subscribe: 创建一个观察者 并监听状态变化
class Store<T> {
  private subject: Subject<T>;
  state: T;

  constructor(state: T) {
    this.subject = new Subject<T>(state);
    this.state = state;
  }

  getState(): T {
    return this.state;
  }

  setState(partial: Partial<T>, replace: boolean = false): void {
    const oldState = this.state;
    const newState = typeof partial === 'function' ? (partial as Function)(this.state) : partial;
    if (Object.is(newState, oldState)) return;

    if (replace) {
      this.state = newState as T;
    } else {
      this.state = { ...oldState, ...newState } as T;
    }

    this.subject.update(this.state);
  }

  subscribe(callback: (newState: T, oldState: T) => void): () => void {
    const watcher = new Watcher<T>(callback);
    this.subject.register(watcher);

    return () => {
      this.subject.remove(watcher);
    };
  }
}

实现createStore

createStore 函数负责创建 store 实例并提供给其他外界组件库使用的

function createStore<T>(callback) {
 const store = new Store<T>();
 store.state = callback(store.setState, store.getState, store.subscribe, store.removeState);
return (selector?): ReturnType<typeof selector> =>  {
   return useStore(store, selector);
 }
}
useStore
实现一 useSyncExternalStore

useStore是react与zustand之间的桥梁。通过订阅 store 的变化,组件在状态更新时可以自动重新渲染

function useStore<T>(store: Store<T>): T;
function useStore<T>(store: Store<T>, selector) : ReturnType<typeof selector>

function useStore<T>(store: Store<T>, selector?: unknown) {
  const getSnapshot = () => ( typeof selector === 'function' ? selector(store.getState()) : store.getState());

  return useSyncExternalStore(
    store.subscribe, // 订阅函数
    getSnapshot,                 // 返回当前状态
    getSnapshot                  // 在服务器渲染中使用相同的快照
  );
}

实现二 useState

在并发渲染模式下,React 的状态订阅需要更加精确地控制。不能确保状态的一致性,解决潜在的状态读取滞后问题,特别是在** React 的并发模式和服务器端渲染(SSR**)中

function useStore<T>(store: Store<T>, selector?: unknown) {
  const state = useRef();
  const selectorFnRef = useRef(selector);
  const selectorFn = selectorFnRef.current;
  const [_,forceUpdate] = useState({});

  store.subscribe((newState, oldState) => {
    if (typeof selectorFn === 'function') {
      const newValue = selectorFn(newState)
      const oldValue = selectorFn(oldState);
      if (newValue !== oldValue) {
        state.current = newValue;
        forceUpdate({})
      }
  } else {
    state.current = newState
     forceUpdate({})
  }
  })

  return state.current;
}

结束语

通过本文的介绍,我们实现了一个基于观察者模式的状态管理系统,并且探讨了 zustand 底层设计的核心思想。zustand 之所以广受欢迎,正是因为它简洁的 API 和轻量的设计,避免了不必要的复杂度,非常适合中小型 React 项目的状态管理需求。

转载自:https://juejin.cn/post/7411355962584449075
评论
请登录