| 46 | * @param externalState React.ComponentState[] |
| 47 | */ |
| 48 | const useInView: UseInView = ( |
| 49 | { root, rootMargin, threshold, unobserveOnEnter, target, onEnter, onLeave } = {}, |
| 50 | externalState = [], |
| 51 | ) => { |
| 52 | const [state, setState] = useState<State>({ |
| 53 | inView: false, |
| 54 | entry: null, |
| 55 | observer: null, |
| 56 | }); |
| 57 | |
| 58 | const callback = useCallback<IntersectionObserverCallback>(([entry], observer) => { |
| 59 | const inThreshold = observer.thresholds.some((t) => entry.intersectionRatio >= t); |
| 60 | const inView = inThreshold && entry.isIntersecting; |
| 61 | |
| 62 | setState({ |
| 63 | inView, |
| 64 | entry, |
| 65 | observer, |
| 66 | }); |
| 67 | |
| 68 | // unobserveOnEnter |
| 69 | if (inView && unobserveOnEnter) { |
| 70 | observer.unobserve(entry.target); |
| 71 | observer.disconnect(); |
| 72 | } |
| 73 | |
| 74 | // Legacy callbacks |
| 75 | if (inView) { |
| 76 | onEnter?.(entry, observer); |
| 77 | } else { |
| 78 | onLeave?.(entry, observer); |
| 79 | } |
| 80 | }, [onEnter, onLeave, unobserveOnEnter]); |
| 81 | |
| 82 | const setTarget = useObserver( |
| 83 | callback, |
| 84 | { root, rootMargin, threshold }, |
| 85 | [unobserveOnEnter, ...externalState], |
| 86 | ); |
| 87 | |
| 88 | // Legacy 'target' option |
| 89 | useEffect(() => { |
| 90 | if (target?.current) setTarget(target.current); |
| 91 | }, [target, setTarget]); |
| 92 | |
| 93 | return [setTarget, state.inView, state.entry, state.observer]; |
| 94 | }; |
| 95 | |
| 96 | export default useInView; |