From 6c532d8e1033579f23a6fec31c98be374d8ab4e1 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Wed, 16 Jun 2021 16:55:44 -0400 Subject: [PATCH 1/3] feat(useIntersectionObserver): add more performant overload --- src/useIntersectionObserver.ts | 47 +++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/useIntersectionObserver.ts b/src/useIntersectionObserver.ts index 7c3c894..89003b8 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. 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 */ -export default function useIntersectionObserver( +function useIntersectionObserver( element: TElement | null | undefined, - { threshold, root, rootMargin }: IntersectionObserverInit = {}, -) { + callback: IntersectionObserverCallback, + options: IntersectionObserverInit, +): void +/** + * Setup an [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) on + * a DOM Element that returns it's entries as they arrive. + * + * @param element The DOM element to observe + * @param init IntersectionObserver options + */ +function useIntersectionObserver( + element: TElement | null | undefined, + options: IntersectionObserverInit, +): IntersectionObserverEntry[] +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 From 1eaa621a5da1545345a7c8df79ca09ec16dcf435 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Wed, 16 Jun 2021 17:02:51 -0400 Subject: [PATCH 2/3] feat(useMutationObserver): add overload to return records and trigger updates --- src/useMutationObserver.ts | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/useMutationObserver.ts b/src/useMutationObserver.ts index a6066c0..e36d3d0 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] @@ -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. + * + * ```ts + * 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 From 2fba7baff8466d904fb5b26f9ec99d59700fdd72 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Wed, 16 Jun 2021 17:03:58 -0400 Subject: [PATCH 3/3] reorder overload --- src/useIntersectionObserver.ts | 14 +++++++------- src/useMutationObserver.ts | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/useIntersectionObserver.ts b/src/useIntersectionObserver.ts index 89003b8..db64b40 100644 --- a/src/useIntersectionObserver.ts +++ b/src/useIntersectionObserver.ts @@ -6,29 +6,29 @@ import useEventCallback from './useEventCallback' /** * 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. + * a DOM Element that returns it's entries as they arrive. * * @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 +): IntersectionObserverEntry[] /** * Setup an [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) on - * a DOM Element that returns it's entries as they arrive. + * 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, -): IntersectionObserverEntry[] +): void function useIntersectionObserver( element: TElement | null | undefined, callbackOrOptions: IntersectionObserverCallback | IntersectionObserverInit, diff --git a/src/useMutationObserver.ts b/src/useMutationObserver.ts index e36d3d0..47a1c45 100644 --- a/src/useMutationObserver.ts +++ b/src/useMutationObserver.ts @@ -17,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) => { @@ -42,7 +42,7 @@ function useMutationObserver( * Observe mutations on a DOM node or tree of DOM nodes. * use a `MutationObserver` and return records as the are received. * - * ```ts + * ```tsx * const [element, attachRef] = useCallbackRef(null); * * const records = useMutationObserver(element, { subtree: true });