Relay.ts

곰튀김·2024년 6월 25일
0

pub/sub 이 필요한 경우. rx의 relay 부분만 따로 구현.

Relay.ts

export class Relay<T> {
  private _current: T;
  private subscribers: Set<(value: T) => void> = new Set();
  private disposeBag = new DisposeBag();

  private constructor(value: T) {
    this._current = value;
  }

  static create<T>(value: T) {
    return new Relay(value);
  }

  map<U>(transform: (value: T) => U): Relay<U> {
    const relay = Relay.create(transform(this._current));
    this.subscribe((value) => (relay.current = transform(value))).disposedBy(
      this.disposeBag,
    );
    return relay;
  }

  flatMap<U>(transform: (value: T) => Relay<U>): Relay<U> {
    const relay = transform(this._current);
    this.subscribe((value) => {
      transform(value).subscribe((value) => (relay.current = value));
    }).disposedBy(this.disposeBag);
    return relay;
  }

  bind(relay: Relay<T>) {
    return this.subscribe((value) => (relay.current = value));
  }

  subscribe(callback: (value: T) => void): Disposable {
    this.subscribers.add(callback);
    callback(this._current);
    return new Disposable(() => {
      this.disposeBag.dispose();
      this.subscribers.delete(callback);
    });
  }

  private reportToSubscribers() {
    this.subscribers.forEach((callback) => callback(this._current));
  }

  set current(value: T) {
    this._current = value;
    this.reportToSubscribers();
  }

  get current() {
    return this._current;
  }
}

DisposeBag.ts

export class Disposable {
  private disposeJob: () => void = () => {};

  constructor(jbo: () => void) {
    this.disposeJob = jbo;
  }

  dispose() {
    this.disposeJob();
    this.disposeJob = () => {};
  }

  disposedBy(bag: DisposeBag) {
    bag.add(this);
  }
}

export class DisposeBag {
  private disposables: Disposable[] = [];

  add(disposable: Disposable) {
    this.disposables.push(disposable);
  }

  dispose() {
    this.disposables.forEach((disposable) => {
      disposable.dispose();
    });
    this.disposables = [];
  }
}

useDisposeBag.ts

export const useDisposeBag = () => {
  const disposeBag = useRef(new DisposeBag());
  useEffect(() => {
    const bag = disposeBag.current;
    return () => bag.dispose();
  }, []);
  return disposeBag.current;
};
profile
사실주의 프로그래머

0개의 댓글