intersectionObserver.tsx 2.6 KB

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