RxJS Observable Share startWith()状态在merge()中

时间:2019-01-20 00:10:19

标签: rxjs observable recompose

如何在3个流之间共享startWith(false)状态?我尝试使用withLatestFrom(),但该值出现了一些奇怪的错误。

const Home = componentFromStream(prop$ => {
  const { handler: toggleHandler, stream: toggle$ } = createEventHandler();
  const { handler: showHandler, stream: show$ } = createEventHandler();
  const { handler: hideHandler, stream: hide$ } = createEventHandler();

  const modal$ = merge(
    toggle$.pipe(
      startWith(false),
      map(() => prev => !prev),
      scan((state, changeState: any) => changeState(state))
    ),
    show$.pipe(
      startWith(false),
      map(() => prev => true),
      scan((state, changeState: any) => changeState(state))
    ),
    hide$.pipe(
      startWith(false),
      map(() => prev => false),
      scan((state, changeState: any) => changeState(state))
    )
  );

  return combineLatest(prop$, modal$).pipe(
    map(([props, modal]) => {
      console.log(modal);
      return (
        <div>
          <button onClick={toggleHandler}>Toggle</button>
          <button onClick={showHandler}>Show</button>
          <button onClick={hideHandler}>Hide</button>
          <h1>{modal ? 'Visible' : 'Hidden'}</h1>
        </div>
      );
    })
  );
});

在该示例中,toggle不考虑showhide的当前值,而仅考虑其最新值。

https://stackblitz.com/edit/react-rxjs6-recompose

3 个答案:

答案 0 :(得分:2)

为此,您将需要对状态进行一些不同的管理,类似于人们在redux中所做的事情。看一个例子:

const { of, merge, fromEvent } = rxjs; // = require("rxjs")
const { map, scan } = rxjs.operators; // = require("rxjs/operators")

const toggle$ = fromEvent(document.getElementById('toggle'), 'click');
const show$ = fromEvent(document.getElementById('show'), 'click');
const hide$ = fromEvent(document.getElementById('hide'), 'click');

const reduce = (state, change) => change(state);

const initialState = false;
const state$ = merge(
  of(e => e),
  toggle$.pipe(map(e => state => !state)),
  show$.pipe(map(e => state => true)),
  hide$.pipe(map(e => state => false)),
).pipe(
  scan(reduce, initialState),
);

state$.subscribe(e => console.log('state: ', e));
<script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>


<button id="toggle">Toggle</button>
<button id="show">Show</button>
<button id="hide">Hide</button>

要更好地了解其工作原理,请查看rxjs文档中的Creating applications文章

答案 1 :(得分:0)

首先在所有这些可观察值上具有startWith意味着它们都将在开始时为false。就像您一次按下所有这些按钮一样。

我认为您应该尝试实现不同的行为。您想使用startWith来设置modal属性的初始状态,对吗?因此,应该在将这些流合并在一起之后出现。

要切换值,您需要做两件事:

  1. 存储状态的位置(通常是scan的累加器)
  2. 区分那些按钮按下。您有三个按钮,因此需要三个值。

这是我的方法:

 const modal$ = merge(
    show$.pipe(mapTo(true)),
    hide$.pipe(mapTo(false)),
    toggle$.pipe(mapTo(null))
  ).pipe(
    startWith(false),
    scan((acc, curr) => {
      if (curr === null) {
        return !acc;
      } else {
        return curr;
      }
    })
  );

“显示”按钮始终发出true,“隐藏”按钮始终发出false,而切换按钮则发出null。我将它们合并在一起,我们想从false开始。接下来是scan,它将状态保存在累加器中。

null到来时它返回否定状态。当truefalse出现时,它会返回它-这样,无论先前的值如何,它都会设置新的状态。

答案 2 :(得分:-1)

我采取了每个人的方法并提出了这个建议。它将toggle()逻辑保留在map函数内,并使用startWith()设置值。

  const modal$ = merge(
    toggle$.pipe(
      map(() => prev => !prev),
    ),
    show$.pipe(
      map(() => prev => true),
    ),
    hide$.pipe(
      map(() => prev => false),
    )
  ).pipe(
    startWith(false),
    scan((state, change) => change(state)),
  );
相关问题