1

I want to use RxJS to listen to clicks, perform a transaction, and keep track of the transaction status:

function performTransaction() {
  const status = {
    '0': 'pending',
    '1': 'success'
  }
  return interval(1000).pipe(take(2), map((i) => status[`${i}`]))
}

const click$ = new Subject<void>()

const claimTxnState$ = click$.pipe(switchMap(() => {
  console.log('performing transaction') // -> runs once which is expected
  return performTransaction()
}))

claimTxnState$.subscribe((v) => {
  console.log('claimTxnState', v);
})

export default function App() {

  return (
    <div className="App">
      <button onClick={() => click$.next()}>click me</button>
    </div>
  );
}

This causes performing transaction to be output once, as is expected.

But I want to pipe claimTxnState$ to more places:

const claimIsPending$ = claimTxnState$.pipe(map((claim) => claim === 'pending'))

claimIsPending$.subscribe((v) => {
  console.log('claimIsPending', v);
})

This now causes performing transaction to be output twice, which I understand because these are cold observables that get recreated on each subscription. But I don't want this. I only want my performTransaction to get called once. How can I achieve this?

Complete example.

sennett
  • 8,014
  • 9
  • 46
  • 69

1 Answers1

0

I found the answer as I was typing the question. I need to use the share operator to convert the observable from cold to hot. This has the effect of sharing claimTxnState$ between all subscribers (i.e. .pipes(...)):

const claimTxnState$ = click$.pipe(
  switchMap(() => {
    console.log('performing transaction')
    return performTransaction()
  }),
  share() // <- this is the interesting line
)

Sandbox.

More detail.

sennett
  • 8,014
  • 9
  • 46
  • 69