1
0

intersectionObserver.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import React, { ReactNode } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { isEqual, isEmpty } from 'lodash';
  4. export interface ReactIntersectionObserverProps {
  5. onIntersect?: IntersectionObserverCallback;
  6. option?: IntersectionObserverInit;
  7. root?: IntersectionObserverInit['root'];
  8. threshold?: IntersectionObserverInit['threshold'];
  9. rootMargin?: IntersectionObserverInit['rootMargin'];
  10. items?: Record<string, Element>;
  11. }
  12. export default class ReactIntersectionObserver extends React.PureComponent<ReactIntersectionObserverProps> {
  13. static propTypes = {
  14. onIntersect: PropTypes.func,
  15. option: PropTypes.object,
  16. root: PropTypes.any,
  17. threshold: PropTypes.number,
  18. rootMargin: PropTypes.string,
  19. items: PropTypes.object,
  20. };
  21. static defaultProps = {
  22. onIntersect: (): void => undefined,
  23. threshold: 0.75,
  24. rootMargin: '0px',
  25. option: {},
  26. items: {},
  27. };
  28. observer: IntersectionObserver;
  29. cachedKeys: Array<string>;
  30. componentDidMount(): void {
  31. const { items } = this.props;
  32. this.cachedKeys = Object.keys(items);
  33. const { root, threshold, rootMargin, option, onIntersect } = this.props;
  34. this.observer = new IntersectionObserver(
  35. onIntersect,
  36. {
  37. root,
  38. threshold,
  39. rootMargin,
  40. ...option,
  41. }
  42. );
  43. this.observeElement();
  44. }
  45. componentDidUpdate(): void {
  46. const { items } = this.props;
  47. const itemKeys = Object.keys(items);
  48. if (!isEqual(this.cachedKeys, itemKeys)) {
  49. this.observeElement(true);
  50. this.cachedKeys = itemKeys;
  51. }
  52. }
  53. componentWillUnmount(): void {
  54. if (this.observer) {
  55. this.observer.disconnect();
  56. this.observer = null;
  57. }
  58. }
  59. observeElement(force = false): void {
  60. const { items } = this.props;
  61. if (isEmpty(items)) {
  62. // stop everything if not defined
  63. this.observer.disconnect();
  64. return;
  65. }
  66. if (force) {
  67. this.observer.disconnect();
  68. }
  69. // observer callback is invoked immediately when observing new elements
  70. Object.keys(items).forEach(key => {
  71. const node = items[key];
  72. if (!node) {
  73. return;
  74. }
  75. this.observer.observe(node);
  76. });
  77. }
  78. render(): ReactNode {
  79. const { children } = this.props;
  80. return children;
  81. }
  82. }