Last active
October 12, 2020 11:36
-
-
Save nicolaschenet/043fc3742837f284411c6fd7254dcb86 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
/** Given a specific ref, returns the Intersection information */ | |
import * as React from 'react'; | |
import type { | |
UseIntersectionOptions, | |
UseIntersectionReturnValue | |
} from './useIntersectionObserver.types.ts'; | |
export const useIntersection = ( | |
target?: Element | null, | |
options?: UseIntersectionOptions | |
): UseIntersectionReturnValue => { | |
const intersectionObserverOptions = { | |
root: null, | |
rootMargin: '0px, | |
threshold: 1, | |
...options | |
}; | |
const [isVisible, setIsVisible] = React.useState<boolean>(false); | |
const [intersectionRatio, setIntersectionRatio] = React.useState<number>(0); | |
const observer = React.useMemo(() => { | |
const handleIntersection: IntersectionObserverCallback = entries => { | |
const entry = entries | |
// We need to sort the entries in a way, we would then find the | |
// latest entry involving our element, otherwise we could use | |
// outdated value. | |
.sort((a, b) => b.time - a.time) | |
.find( | |
(event: IntersectionObserverEntry) => | |
event.target === target | |
); | |
if (entry) { | |
const { | |
isIntersecting, | |
intersectionRatio: entryIntersectionRatio | |
} = entry; | |
setIsVisible(isIntersecting); | |
setIntersectionRatio(entryIntersectionRatio); | |
} | |
}; | |
return new IntersectionObserver(handleIntersection, { | |
root: intersectionObserverOptions.root, | |
rootMargin: intersectionObserverOptions.rootMargin, | |
threshold: intersectionObserverOptions.threshold | |
}); | |
// This weirdly exhaustive dependency declaration avoids an infinite loop | |
// that would occur if we just used `intersectionObserverOptions` | |
}, [ | |
intersectionObserverOptions.root, | |
intersectionObserverOptions.rootMargin, | |
intersectionObserverOptions.threshold, | |
target | |
]); | |
React.useEffect(() => { | |
if (!target) { | |
return; | |
} | |
observer.observe(target); | |
return () => observer.unobserve(target); | |
}, [target, observer]); | |
return [isVisible, intersectionRatio]; | |
}; |
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
type Visibility = boolean; | |
type IntersectionValue = number; | |
export interface UseIntersectionOptions { | |
root: Element | null; | |
rootMargin?: string; | |
threshold?: number; | |
} | |
export type UseIntersectionReturnValue = [Visibility, IntersectionValue]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment