diff options
Diffstat (limited to 'src/utils/hooks/use-mutation-observer')
3 files changed, 78 insertions, 0 deletions
diff --git a/src/utils/hooks/use-mutation-observer/index.ts b/src/utils/hooks/use-mutation-observer/index.ts new file mode 100644 index 0000000..16780fe --- /dev/null +++ b/src/utils/hooks/use-mutation-observer/index.ts @@ -0,0 +1 @@ +export * from './use-mutation-observer'; diff --git a/src/utils/hooks/use-mutation-observer/use-mutation-observer.test.ts b/src/utils/hooks/use-mutation-observer/use-mutation-observer.test.ts new file mode 100644 index 0000000..62ed559 --- /dev/null +++ b/src/utils/hooks/use-mutation-observer/use-mutation-observer.test.ts @@ -0,0 +1,42 @@ +import { beforeEach, describe, expect, it, jest } from '@jest/globals'; +import { renderHook } from '@testing-library/react'; +import { useMutationObserver } from './use-mutation-observer'; + +describe('useMutationObserver', () => { + beforeEach(() => { + jest.restoreAllMocks(); + }); + + it('can create a new observer', () => { + const callback = jest.fn(); + const observerSpy = jest.spyOn(MutationObserver.prototype, 'observe'); + const wrapper = document.createElement('div'); + const options: MutationObserverInit = { childList: true }; + + renderHook(() => + useMutationObserver({ + callback, + options, + ref: { current: wrapper }, + }) + ); + + expect(observerSpy).toHaveBeenCalledTimes(1); + expect(observerSpy).toHaveBeenCalledWith(wrapper, options); + }); + + it('does not create a new observer when ref is null', () => { + const callback = jest.fn(); + const observerSpy = jest.spyOn(MutationObserver.prototype, 'observe'); + + renderHook(() => + useMutationObserver({ + callback, + options: { childList: true }, + ref: { current: null }, + }) + ); + + expect(observerSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/src/utils/hooks/use-mutation-observer/use-mutation-observer.ts b/src/utils/hooks/use-mutation-observer/use-mutation-observer.ts new file mode 100644 index 0000000..6043055 --- /dev/null +++ b/src/utils/hooks/use-mutation-observer/use-mutation-observer.ts @@ -0,0 +1,35 @@ +import { type RefObject, useEffect } from 'react'; +import type { Nullable } from '../../../types'; + +type UseMutationObserverProps<T extends Nullable<HTMLElement>> = { + /** + * A callback to execute when mutations are observed. + */ + callback: MutationCallback; + /** + * The options passed to mutation observer. + */ + options: MutationObserverInit; + /** + * A reference to the DOM node to observe. + */ + ref: RefObject<T>; +}; + +export const useMutationObserver = <T extends Nullable<HTMLElement>>({ + callback, + options, + ref, +}: UseMutationObserverProps<T>) => { + useEffect(() => { + if (!ref.current) return undefined; + + const observer = new MutationObserver(callback); + + observer.observe(ref.current, options); + + return () => { + observer.disconnect(); + }; + }, [callback, options, ref]); +}; |
