Skip to content

Instantly share code, notes, and snippets.

@imbhargav5
Created October 2, 2019 17:32
Show Gist options
  • Save imbhargav5/e9f3e3418e6a84c87b84eea401e5fa9e to your computer and use it in GitHub Desktop.
Save imbhargav5/e9f3e3418e6a84c87b84eea401e5fa9e to your computer and use it in GitHub Desktop.
import * as React from "react";
import { useDidMount } from "shared/useDidMount";
const initialState = {
intersectionObj: {},
observerInState: null,
isVisible: false
};
interface Iaction {
type: string;
data: any;
}
function IntersectionObserverReducer(state: any, action: Iaction) {
switch (action.type) {
case "SETINTERSECTIONOBJ": {
return {
...state,
intersectionObj: action.data
};
}
case "SETOBSERVERHANDLE": {
return {
...state,
observerInState: action.data
};
}
case "SET_VISIBILITY": {
return {
...state,
isVisible: action.data
};
}
}
}
const checkFeasibility = () => {
let MyWindow = window as any;
if (!MyWindow || !MyWindow.IntersectionObserver) {
console.warn(
"Intersection Observer is not supported in the current browser / environment"
);
return false;
}
return true;
};
interface IOptions {
root: HTMLElement;
rootMargin?: string;
threshold?: number[];
when?: boolean;
checkVisibilityPredicate?: (
entry: IntersectionObserverEntry,
observer: IntersectionObserver
) => boolean;
}
type useIntersectionObserverReturn = [
(node: HTMLElement) => void,
boolean,
IntersectionObserverEntry,
IntersectionObserver
];
/***
* To use the the intersection Observer
* visibiltyCondition call back can sent , which will be having access to
* intersection entry object
* Read https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
* about various attributes provided by entries
* Each entry describes an intersection change for one observed
* target element:
* entry.boundingClientRect
* entry.intersectionRatio
* entry.intersectionRect
* entry.isIntersecting
* entry.rootBounds
* entry.target
* entry.time
*/
const defaultCheckVisibilityPredicate = (entry: IntersectionObserverEntry) => {
if (entry.intersectionRatio >= 1) {
return true;
}
return false;
};
function useIntersectionObserver(
options: IOptions
): useIntersectionObserverReturn {
const defaultOptions = {
rootMargin: "0px 0px 0px 0px",
threshold: [0, 1],
when: true,
checkVisibilityPredicate: defaultCheckVisibilityPredicate
};
const {
root = null,
rootMargin,
threshold,
when,
checkVisibilityPredicate
} = {
...defaultOptions,
...options
};
const [element, setElement] = React.useState<HTMLElement>(null);
const [state, dispatch] = React.useReducer(
IntersectionObserverReducer,
initialState
);
const { intersectionObj, isVisible } = state;
const observerRef = React.useRef(null);
const checkVisibilityPredicateRef = React.useRef(checkVisibilityPredicate);
React.useEffect(() => {
checkVisibilityPredicateRef.current = checkVisibilityPredicate;
});
useDidMount(() => {
checkFeasibility();
});
const handleIntersectionObserve = React.useCallback(
(entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
entries.forEach((entry: IntersectionObserverEntry) => {
// setIntersectionObj(entry);
dispatch({
type: "SETINTERSECTIONOBJ",
data: entry
});
const _visibilityFn =
checkVisibilityPredicateRef.current ||
defaultCheckVisibilityPredicate;
dispatch({
type: "SET_VISIBILITY",
data: _visibilityFn(entry, observer)
});
});
},
[]
);
/**
* Effect responsible for creating intersection observer and
* registering the observer for specific element
*/
React.useEffect(() => {
if (when) {
const observer = new IntersectionObserver(handleIntersectionObserve, {
root,
threshold,
rootMargin
});
observerRef.current = observer;
if (element && observerRef.current) {
observerRef.current.observe(element);
return () => {
if (element && observerRef.current) {
observerRef.current.unobserve(element);
}
};
}
}
}, [rootMargin, when, threshold]);
const callbackRef = React.useCallback((node: HTMLElement) => {
setElement(node);
}, []);
return [callbackRef, isVisible, intersectionObj, observerRef.current];
}
export { useIntersectionObserver };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment