[RxJS] RxJS Advanced Patterns Operate Heavily Dynamic UIs

 Check the playground.

复制代码
import {Counter, CountDownState, ConterStateKeys, PartialCountDownState} from './counter'
import { Subject, Observable, merge, timer, NEVER, combineLatest} from 'rxjs'; 
import { map, mapTo, switchMap, pluck, scan, startWith,shareReplay, distinctUntilChanged, withLatestFrom, tap} from 'rxjs/operators';

// EXERCISE DESCRIPTION ==============================

/**
 * Use `ConterStateKeys` for property names.
 * Explort the counterUI API by typing `counterUI.` somewhere. ;)
 * 
 * Implement all features of the counter: 
 * 1. Start, pause the counter. Then restart the counter with 0 (+)  
 * 2. Start it again from paused number (++) 
 * 3. If Set to button is clicked set counter value to input value while counting (+++)
 * 4. Reset to initial state if reset button is clicked (+)
 * 5. If count up button is clicked count up, if count down button is clicked count down  (+)
 * 6. Change interval if input tickSpeed input changes (++)
 * 7. Change count up if input countDiff changes (++)
 * 8. Take care of rendering execution and other performance optimisations as well as refactoring (+)
 */

// ==================================================================

// = BASE OBSERVABLES  ====================================================
// == SOURCE OBSERVABLES ==================================================
const initialConterState: CountDownState = {
  isTicking: false, 
  count: 0, 
  countUp: true, 
  tickSpeed: 200, 
  countDiff:1
};

const counterUI = new Counter(
  document.body,
  {
    initialSetTo: initialConterState.count + 10,
    initialTickSpeed: initialConterState.tickSpeed,
    initialCountDiff: initialConterState.countDiff,
  }
);

// === STATE OBSERVABLES ==================================================
const programmaticCommandSubject = new Subject<PartialCountDownState>();
const counterCommands$ = merge(
  counterUI.btnStart$.pipe(mapTo({isTicking: true})), 
  counterUI.btnPause$.pipe(mapTo({isTicking: false})),
  counterUI.btnSetTo$.pipe(map(n => ({count: n}))),
  counterUI.btnUp$.pipe(mapTo({countUp: true})),
  counterUI.btnDown$.pipe(mapTo({countUp: false})),
  counterUI.btnReset$.pipe(mapTo({...initialConterState})),
  counterUI.inputTickSpeed$.pipe(map ( n => ({tickSpeed: n}))),
  counterUI.inputCountDiff$.pipe(map ( n => ({countDiff: n}))),
  programmaticCommandSubject.asObservable()
);

const counterState$ = counterCommands$
.pipe(
  startWith(initialConterState),
  scan((state, command) => ({
    ...state,
    ...command
  })),
  shareReplay(1)
);

// === INTERACTION OBSERVABLES ============================================
const count$ = counterState$.pipe(
  queryState('count')
);

const isTicking$ = counterState$.pipe(
  queryState('isTicking')
);
const intervalTick$ = isTicking$.pipe(
  switchMap(isTicking => isTicking ? timer(0, initialConterState.tickSpeed): NEVER)
);
// = SIDE EFFECTS =========================================================
// == UI INPUTS ===========================================================
const renderCountChange$ = count$
.pipe(
  tap((n: number) => counterUI.renderCounterValue(n))
);

// == UI OUTPUTS ==========================================================
const commandFromTick$ = intervalTick$
  .pipe(
     withLatestFrom(count$, (_, count) => count),
     tap((count: number) => programmaticCommandSubject.next({count: ++count}))
  );

// == SUBSCRIPTION ========================================================

merge(
  // Input side effect
  renderCountChange$,
  // Outputs side effect
  commandFromTick$
)
.subscribe();


// == CREATION METHODS ====================================================
function queryState (name) {
  return function (o$) {
    return o$.pipe(
      pluck(name),
      distinctUntilChanged()
    )
  }
}
复制代码

 

posted @   Zhentiw  阅读(502)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2016-06-03 [Redux] Supplying the Initial State
2015-06-03 [D3] 6. Color Scale
2015-06-03 [D3] 5 .rangeBands
2015-06-03 [D3] 4. d3.max
2015-06-03 [D3] 3. Scaling Basics
2015-06-03 [D3] 2. Basics of SVG
2013-06-03 【PHP 】 伪静态 - 2. 使用apahce的rewrite机制(1) 开启rewrite模块,配置虚拟主机,对目录访问权限的修改
点击右上角即可分享
微信分享提示