Created
October 2, 2019 17:32
-
-
Save imbhargav5/e9f3e3418e6a84c87b84eea401e5fa9e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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