首页 / 模块目录 / HUD / UI / UiStateModel

UiStateModel

纯逻辑

可观察的 UI 状态更新,带稳定快照。

类别HUD / UI
依赖档位纯逻辑
内部依赖
相关模块
只取这个模块
modules/user-interface/UiStateModel.js

连同其内部依赖一并复制,保留相对目录结构。

源码

function cloneState(state) {
  return { ...state };
}

export class UiStateModel {
  constructor(
    initialState = {},
    emitInitial = false,
    equality = Object.is
  ) {
    this.state = cloneState(initialState);
    this.emitInitial = emitInitial;
    this.equality = equality;
    this.listeners = new Set();
  }

  getState() {
    return cloneState(this.state);
  }

  subscribe(listener, emitInitial = this.emitInitial) {
    if (typeof listener !== 'function') {
      throw new Error('UiStateModel.subscribe: listener must be a function');
    }

    this.listeners.add(listener);
    if (emitInitial) {
      listener(this.getState(), Object.keys(this.state));
    }

    return () => this.listeners.delete(listener);
  }

  patch(partialState = {}) {
    const nextState = cloneState(this.state);
    const changedKeys = [];

    for (const [key, value] of Object.entries(partialState)) {
      if (this.equality(this.state[key], value)) continue;
      nextState[key] = value;
      changedKeys.push(key);
    }

    if (changedKeys.length === 0) return [];

    this.state = nextState;
    const snapshot = this.getState();
    for (const listener of this.listeners) {
      listener(snapshot, changedKeys);
    }

    return changedKeys;
  }

  replace(nextState = {}) {
    const keys = new Set([
      ...Object.keys(this.state),
      ...Object.keys(nextState),
    ]);
    const changedKeys = [];

    for (const key of keys) {
      if (this.equality(this.state[key], nextState[key])) continue;
      changedKeys.push(key);
    }

    if (changedKeys.length === 0) return [];

    this.state = cloneState(nextState);
    const snapshot = this.getState();
    for (const listener of this.listeners) {
      listener(snapshot, changedKeys);
    }

    return changedKeys;
  }
}