Issue
I'm currently using RxJS in an angular project. In this project, I have a list that should act as a to-do list :
- In the beginning, the list is filled with 5 items.
- The user can remove an element.
- When an element is removed, a new element is fetched.
- When the list is empty, new elements are fetched.
I managed to do so using 3 observables and 1 state:
// api.fetchElement -> ({ count: number, exclude: string[] }) => Observable<Element[]>
// removedElements$ -> Subject<string>
let elements = []
const initialElements$ = api.fetchElement({ count: 5 })
// removedElementId$ is a subject of string
// in which element ids are produced.
const reloadedElements$ = removedElementId$.pipe(
// My API is eventualy consistent, so I need to exclude the removed items
// for a short period.
mergeMap(id => api.fetchElement({ count: 5, exclude: [ id ] })
)
const reloadedElementsWhenEmptyList$ = interval(3000).pipe(
filter(() => elements.length < 5),
mergeMap(() => api.fetchElement({ count: 5 - elements.length, exclude: elements.map(e => e.id) })
)
initialElements$.subscribe({
next: newElements => elements = newElements
})
reloadedElements$.subscribe({
next: newElements => elements = newElements
})
reloadedElementsWhenEmptyList$.subscribe({
next: newElements => elements = newElements
})
The current implementation works, but I'm wondering if there is a way to do the same behaviour but without having to use the final state elements
in the observable operators?
I've searched for operators in the RxJS docs, but I failed to find any operators or combinations of operators that satisfy my needs.
Solution
Your settup is a bit bizarre and I can't help but wonder if there's a better way to structure this. Sadly, there's just not enough here to know.
Either way, you can rid yourself of the global state elements
by creating an observable that emits the most recent elements
array when subscribed. In this example implementation I've called it elements$
const initialElements$ = api.fetchElement({ count: 5 });
// removedElementId$ is a subject of string
// in which element ids are produced.
const reloadedElements$ = removedElementId$.pipe(
// My API is eventualy consistent, so I need to exclude the removed items
// for a short period.
mergeMap(id => api.fetchElement({
count: 5,
exclude: [ id ]
}))
);
const reloadedElementsWhenEmptyList$ = interval(3000).pipe(
switchMap(_ => elements$),
filter(elements => elements.length < 5),
mergeMap(elements => api.fetchElement({
count: 5 - elements.length,
exclude: elements.map(e => e.id)
}))
);
const elements$ = merge(
initialElements$,
reloadedElements$,
reloadedElementsWhenEmptyList$
).pipe(
startWith([]),
shareReplay(1)
);
You'll notice that when I needed access to your old elements array, I just subscribed to elements$
.
If you're using TypeScript, it might take some extra work to make sure your types align here. Again, there's not enough info in your example to do this as writ, but I'll leave that exercise to you.
Answered By - Mrk Sef
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.