-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.tsx
85 lines (73 loc) · 2.48 KB
/
index.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { useEffect, useRef } from 'react';
import { ElementType, ReactNode } from 'react';
/**
* @member className - className that deliver to container
* @member items - items that should be rendered
* @member idPrefix - idPrefix is used to observe last item
* @member fetchMore - when last item is visible on viewport then fetchMore invoked
* @member render - this method specify how to render individual item
* @member container - container that contains rendered items
*/
export interface InifiniteScrollerProps {
className?: string;
items: any[];
idPrefix: string;
fetchMore: any;
render: (item: any, index: number) => ReactNode;
container: ElementType;
}
/**
* @property {ElementType} container - container that contains rendered items
* @property {Array<any>} items - items that should be rendered
* @property {string} idPrefix is used to observe last item
* @property {function} fetchMore - when last item is visible on viewport then fetchMore invoked
* @property {function} render - this method specify how to render items rendered DOM elements should contain id attribute
* that starts with idPrefix and followed by hyphen and index(idPrefix-index form) so that InfiniteScroller can observe last items
* @property {string} className - className that deliver to container
*/
const InfiniteScroller = ({
container = 'div',
items,
idPrefix,
fetchMore,
render,
className = '',
}: InifiniteScrollerProps) => {
const observerRef = useRef<IntersectionObserver>();
useEffect(() => {
const lastElement = document.getElementById(
`${idPrefix}-${items.length - 1}`
);
// if lastElement doesn't exist in the DOM tree
// infinite scroller won't be fetch more data
if (!lastElement) {
return;
}
const observerCallback: IntersectionObserverCallback = (entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
fetchMore();
}
});
};
const observerOptions: IntersectionObserverInit = {
threshold: 0,
};
observerRef.current = new IntersectionObserver(
observerCallback,
observerOptions
);
observerRef.current.observe(lastElement);
return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
};
}, [idPrefix, fetchMore, items.length]);
if (!render) {
return null;
}
const Container = container;
return <Container className={className}>{items.map(render)}</Container>;
};
export default InfiniteScroller;