简体   繁体   中英

Managing internal state in an RxJS observable

I'm new to RxJS and wondering about internal state.

Our use case is a promise that returns an initial state (a list of items with ids) and an observable that streams mutations (deleted ids) to that state. The updated list of items is then a persistent state and should be used in building the result.

I can hack something together using a global variable but as expected it behaves weirdly:

import { combineLatest, from } from "rxjs";
import { startWith } from "rxjs/operators";

...

const initialObservable = from(initialPromise);

const mutationObservableWithStart = mutationObservable.pipe(startWith(null));

const globalDeletedIds: string[] = [];

return combineLatest([initialObservable, mutationObservableWithStart]).pipe(
  map(([items, mutation]) => {
    if (mutation && mutation.deleted) {
      globalDeletedIds.push(mutation.id);
    }

    const currentItems = items.filter(doesNotIncludeIds(globalDeletedIds));

    return buildResult(currentItems);
  })
);

My question is: how to correctly manage internal state in an observable?

If I understand the problem right, I would proceed like this (inline comments try to explain the logic)

// first we start the stream with the initial list of items (an array notified
// by the initialObservable)
initialObservable.pipe(
  // once initialObservable notifies the list we switch to another observable
  switchMap(items => {
    // the stream we switch to starts with the stream of mutations
    return mutationObservable.pipe(
      // we need to notify something since the very beginning, not only from
      // when the first mutation occurs, therefore we use startWith to say
      // that the new stream starts immediately notifying a null (assuming 
      // there is no null key in items)
      startWith(null),
      // since we need to keep a state but also to notify the state any time 
      // an event from upstream (i.e. a mutation) occurs, then we use the
      // scan operator which holds an internal state which is notified any time
      // upstream notifies something
      scan((state, m) => {
        // the state, i.e. the list of items, gets updated according to the
        //  mutations received from upstream
        state = state.filter(item => item.id !== m)
        // scan returns a new state
        return state
      }, items)  // the state is initialized with the items received
    )
  })
)

You can take a look at this stackblitz for an example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM