RxJS: How to use startWith with pairwise
source link: https://advancedweb.hu/rxjs-how-to-use-startwith-with-pairwise/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
pairwise
and startWith
The pairwise
operator is a great tool if you also need the previous value for every element. It uses a buffer with a size of 2, making it memory efficient as well. The problem is that it does not emit on the first element, making its results one item shorter than the input.
rxjs.of(1, 2, 3).pipe(
rxjs.operators.pairwise()
).subscribe(console.log.bind(console));
// [1, 2]
// [2, 3]
123pairwise1223
Depending on the use-case, it might be a good thing. But if you need an element for every input item, combine it with the startWith
operator:
rxjs.of(1, 2, 3).pipe(
rxjs.operators.startWith(0),
rxjs.operators.pairwise()
).subscribe(console.log.bind(console));
// [0, 1]
// [1, 2]
// [2. 3]
123startWith(0)0123pairwise011223
Calculating differences
This helps when you need to calculate differences with a known start point. For example, let’s say something starts from the 0 coordinate and the stream consists of points it goes to. Using pairwise
in this case offers an easy way to calculate the differences:
rxjs.of(10,50,40).pipe(
rxjs.operators.pairwise(),
rxjs.operators.map(([from, to]) => Math.abs(from - to)),
).subscribe(console.log.bind(console));
// 40, 10
105040pairwise10505040map4010
In this case, to know how far it moved, you need also to add the starting point of 0 with the startWith
operator:
rxjs.of(10,50,40).pipe(
rxjs.operators.startWith(0),
rxjs.operators.pairwise(),
rxjs.operators.map(([from, to]) => Math.abs(from - to)),
).subscribe(console.log.bind(console));
// 10, 40, 10
105040startWith0105040pairwise01010505040map104010
When not to use startWith
with pairwise
Let’s say you want to draw circles on mouse clicks and also want to connect them with lines. Since lines need a start and an end coordinate, pairwise
is a good operator for them. On the other hand, circles only need the current mouse coordinates.
It’s tempting to combine drawing the two shapes in a subscription:
const clicks = rxjs.fromEvent(svg, "click").pipe(
rxjs.operators.map((e) => ({x: e.offsetX, y: e.offsetY})),
);
// broken: won't draw a circle on the first click
clicks.pipe(
rxjs.operators.pairwise(),
)
.subscribe(([p1, p2]) => {
drawLine(p1, p2);
drawCircle(p2);
})
The above implementation does not draw a circle on the first click as pairwise
does not emit an element for that. A quick fix is to use pairwise
and check the edge case when the first element is undefined:
clicks.pipe(
rxjs.operators.startWith(undefined),
rxjs.operators.pairwise(),
)
.subscribe(([p1, p2]) => {
if (p1 !== undefined) {
drawLine(p1, p2);
}
drawCircle(p2);
})
The problem with this is that it meshes two things, one that needs elements and one that needs pairs. A better solution is to separate the two and only use pairwise
for the latter:
clicks.pipe(
rxjs.operators.pairwise(),
)
.subscribe(([p1, p2]) => {
drawLine(p1, p2);
});
clicks.subscribe((p) => drawCircle(p));
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK