I'm building an infinite scroller in cycle.js. I have an ajax service that returns X results per page. The first request is with a page id of 0, but subsequent requests need to use the page id returned in the first result set. The results are placed in the dom. When the user scrolls to the bottom of the visible results, a new set is loaded and concatenated to the list. I have something that works, but not as well as I would like.
The shortened version looks like:
const onScroll$ = sources.DOM.select('document').events('scroll')
.map((e) => {
return e.srcElement.scrollingElement.scrollTop;
})
.compose(debounce(1000));
const onHeight$ = sources.DOM.select('#items').elements()
.map((e) => {
return e[0].scrollHeight;
})
const scroll$ = onScroll$.startWith(0);
const height$ = onHeight$.startWith(0);
const itemsXHR$ = sources.HTTP.select('items')
.flatten();
const id$ = itemsXHR$
.map(res => res.body.data.content.listPageId)
.take(2)
.startWith(0);
const getitems$ = xs.combine(scroll$,height$,id$)
.map( ( [scrollHeight, contentHeight, sid, lp] ) => {
if ( scrollHeight > (contentHeight - window.innerHeight - 1) ) {
const ms = new Date().getTime();
return {
url: `data/${sid}?_dc=${ms}`,
category: 'items',
method: 'GET'
};
}
return {};
});
const items$ = itemsXHR$
.fold( (acc=[],t) => [...acc, ...t.body.data.content.items] )
.startWith(null);
const vdom$ = items$.map(items =>
div('#items', [
ul('.search-results', items==null ? [] : items.map(data =>
li('.search-result', [
a({attrs: {href: `/${data.id}`}}, [
img({attrs: {src: data.url}})
])
])
))
])
);
The main issue is that the ajax request is linked to scroll position, and its possible that to fire multiple requests during scroll.
As the moment I debounce the scroll, but that it not ideal here.
Any ideas on how to orchestrate the streams so that only 1 request is sent when needed (when the user is near the bottom of the page)?
I though maybe a unique id per page and use .dropRepeats on the getitem$? I don't have a unique page id in the result though.