diff --git a/src/useIntersectionObserver.ts b/src/useIntersectionObserver.ts index 7c3c894..db64b40 100644 --- a/src/useIntersectionObserver.ts +++ b/src/useIntersectionObserver.ts @@ -2,30 +2,61 @@ import { useState } from 'react' import useStableMemo from './useStableMemo' import useEffect from './useIsomorphicEffect' +import useEventCallback from './useEventCallback' /** * Setup an [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) on - * a DOM Element. + * a DOM Element that returns it's entries as they arrive. * * @param element The DOM element to observe * @param init IntersectionObserver options */ -export default function useIntersectionObserver( +function useIntersectionObserver( element: TElement | null | undefined, - { threshold, root, rootMargin }: IntersectionObserverInit = {}, -) { + options: IntersectionObserverInit, +): IntersectionObserverEntry[] +/** + * Setup an [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) on + * a DOM Element. This overload does not trigger component updates when receiving new + * entries. This allows for finer grained performance optimizations by the consumer. + * + * @param element The DOM element to observe + * @param callback A listener for intersection updates. + * @param init IntersectionObserver options + */ +function useIntersectionObserver( + element: TElement | null | undefined, + callback: IntersectionObserverCallback, + options: IntersectionObserverInit, +): void +function useIntersectionObserver( + element: TElement | null | undefined, + callbackOrOptions: IntersectionObserverCallback | IntersectionObserverInit, + maybeOptions?: IntersectionObserverInit, +): void | IntersectionObserverEntry[] { + let callback: IntersectionObserverCallback | undefined + let options: IntersectionObserverInit + if (typeof callbackOrOptions === 'function') { + callback = callbackOrOptions + options = maybeOptions || {} + } else { + options = callbackOrOptions || {} + } + const { threshold, root, rootMargin } = options const [entries, setEntry] = useState(null) + const handler = useEventCallback(callback || setEntry) + const observer = useStableMemo( () => typeof IntersectionObserver !== 'undefined' && - new IntersectionObserver(entries => setEntry(entries), { + new IntersectionObserver(handler, { threshold, root, rootMargin, }), - [root, rootMargin, threshold && JSON.stringify(threshold)], + [handler, root, rootMargin, threshold && JSON.stringify(threshold)], ) useEffect(() => { @@ -38,5 +69,7 @@ export default function useIntersectionObserver( } }, [observer, element]) - return entries || [] + return callback ? undefined : entries || [] } + +export default useIntersectionObserver diff --git a/src/useMutationObserver.ts b/src/useMutationObserver.ts index a6066c0..47a1c45 100644 --- a/src/useMutationObserver.ts +++ b/src/useMutationObserver.ts @@ -2,6 +2,7 @@ import useCustomEffect from './useCustomEffect' import { dequal } from 'dequal' import useImmediateUpdateEffect from './useImmediateUpdateEffect' import useEventCallback from './useEventCallback' +import { useState } from 'react' type Deps = [Element | null | undefined, MutationObserverInit] @@ -16,7 +17,7 @@ function isDepsEqual( * Observe mutations on a DOM node or tree of DOM nodes. * Depends on the `MutationObserver` api. * - * ```ts + * ```tsx * const [element, attachRef] = useCallbackRef(null); * * useMutationObserver(element, { subtree: true }, (records) => { @@ -36,8 +37,35 @@ function useMutationObserver( element: Element | null | undefined, config: MutationObserverInit, callback: MutationCallback, -): void { - const fn = useEventCallback(callback) +): void +/** + * Observe mutations on a DOM node or tree of DOM nodes. + * use a `MutationObserver` and return records as the are received. + * + * ```tsx + * const [element, attachRef] = useCallbackRef(null); + * + * const records = useMutationObserver(element, { subtree: true }); + * + * return ( + *
+ * ) + * ``` + * + * @param element The DOM element to observe + * @param config The observer configuration + */ +function useMutationObserver( + element: Element | null | undefined, + config: MutationObserverInit, +): MutationRecord[] +function useMutationObserver( + element: Element | null | undefined, + config: MutationObserverInit, + callback?: MutationCallback, +): MutationRecord[] | void { + const [records, setRecords] = useState(null) + const handler = useEventCallback(callback || setRecords) useCustomEffect( () => { @@ -47,7 +75,7 @@ function useMutationObserver( // observing again _should_ disable the last listener but doesn't // seem to always be the case, maybe just in JSDOM? In any case the cost // to redeclaring it is gonna be fairly low anyway, so make it simple - const observer = new MutationObserver(fn) + const observer = new MutationObserver(handler) observer.observe(element, config) @@ -63,6 +91,8 @@ function useMutationObserver( effectHook: useImmediateUpdateEffect, }, ) + + return callback ? void 0 : records || [] } export default useMutationObserver