This tutorial assumes you have gone through the Github Issues Viewer
tutorial, and that you are already familiar with both RxJS and React.
The purpose of the tutorial is to introduce the most important APIs of the
@react-rxjs/utils package, and for that we are going to build a simple todo-list
application. Our app will be able to do the following:
The first thing that we should do is to capture the events triggered by the user.
Let's create a few Subjects for this. Also, it's probably best if our presentational
components don't know about the existence of these Subjects. So we will also create
a set of functions that capture the user-evens and push then into the Subjects:
import{Subject}from"rxjs"
const newTodo$ =newSubject<string>()
exportconstonNewTodo=(text: string)=> text && newTodo$.next(text)
It would be very convenient to have a merged stream with all those events. However,
if we did a traditional merge, then it would be very challenging to know the
origin of each event.
That's why @react-rxjs/utils exposes the mergeWithKey
operator. Let's use it:
const todoActions$ =mergeWithKey({
add: newTodo$.pipe(map((text, id)=>({ id, text }))),
edit: editTodo$,
toggle: toggleTodo$.pipe(map(id=>({ id }))),
delete: deleteTodo$.pipe(map(id=>({ id })))
})
Which is basically the same as doing this (but a lot shorter, of course ๐):
Now that we have put all the streams together, let's create a stream for
each todo. And for that, we will be using another operator from @react-rxjs/utils:
the split operator:
As you can see split is very similar to the groupBy operator that's exposed
from RxJS. However, there are some important differences:
The first difference is that split doesn't have a "duration selector" argument
for determining the duration of an inner stream. Once an inner stream completes, split
will forget about it, meaning that it will remove it from its internal cache.
Therefore, if afterwords the source emits a value with the same key, then split
will create (and emit) a new GroupedObservable.
Another important difference is the second argument of split, which allows you
to create a complex inner stream that will become the "grouped" stream that is emitted.
Also, this returned stream is enhanced with a shareReplay(1), and split internally
subscribes to it as soon as it is created to ensure that the consumer always has the
latest value.
Our todos$ variable is an Observable of GroupedObservables<number, Todo> and
that in itself is not very useful. It would be a lot more convenient to have an Observable
of Map<number, Todo>. Which is exactly what the collectValues
operator is for. Let's try it: