diff --git a/src/useMutateObserver.tsx b/src/useMutateObserver.tsx index bdeaa20..4349802 100644 --- a/src/useMutateObserver.tsx +++ b/src/useMutateObserver.tsx @@ -1,4 +1,4 @@ -import { canUseDom } from '@rc-component/util'; +import { canUseDom, useEvent } from '@rc-component/util'; import React from 'react'; const defaultOptions: MutationObserverInit = { @@ -9,15 +9,12 @@ const defaultOptions: MutationObserverInit = { const useMutateObserver = ( nodeOrList: - | HTMLElement - | HTMLElement[] - | SVGElement - | SVGElement[] - | null - | undefined, + HTMLElement | HTMLElement[] | SVGElement | SVGElement[] | null | undefined, callback: MutationCallback, options: MutationObserverInit = defaultOptions, ) => { + const mergedCallback = useEvent(callback); + React.useEffect(() => { if (!canUseDom() || !nodeOrList) { return; @@ -28,7 +25,7 @@ const useMutateObserver = ( const nodeList = Array.isArray(nodeOrList) ? nodeOrList : [nodeOrList]; if ('MutationObserver' in window) { - instance = new MutationObserver(callback); + instance = new MutationObserver(mergedCallback); nodeList.forEach(element => { instance.observe(element, options); @@ -38,7 +35,7 @@ const useMutateObserver = ( instance?.takeRecords(); instance?.disconnect(); }; - }, [options, nodeOrList]); + }, [options, nodeOrList, mergedCallback]); }; export default useMutateObserver; diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 6e9acc0..5420d86 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -1,6 +1,6 @@ import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; -import MutateObserver from '../src'; +import MutateObserver, { useMutateObserver } from '../src'; jest.mock('../src/useMutateObserver', () => { const origin = jest.requireActual('../src/useMutateObserver').default; @@ -110,4 +110,42 @@ describe('MutateObserver', () => { expect(global.mutateTargetElement).toBe(container.querySelector('.bamboo')); }); + + it('should use the latest mutation callback', async () => { + const target = document.createElement('div'); + const firstCallback = jest.fn(); + const latestCallback = jest.fn(); + const NativeMutationObserver = window.MutationObserver; + const observerInstances: MutationObserver[] = []; + const observerConstructor = jest + .spyOn(window, 'MutationObserver') + .mockImplementation(callback => { + const instance = new NativeMutationObserver(callback); + observerInstances.push(instance); + return instance; + }); + + const Demo: React.FC<{ callback: MutationCallback }> = ({ callback }) => { + useMutateObserver(target, callback); + return null; + }; + + const { rerender, unmount } = render(); + expect(observerConstructor).toHaveBeenCalledTimes(1); + const disconnect = jest.spyOn(observerInstances[0], 'disconnect'); + + rerender(); + expect(observerConstructor).toHaveBeenCalledTimes(1); + expect(disconnect).not.toHaveBeenCalled(); + target.className = 'updated'; + + await waitFor(() => { + expect(latestCallback).toHaveBeenCalledTimes(1); + }); + expect(firstCallback).not.toHaveBeenCalled(); + + unmount(); + expect(disconnect).toHaveBeenCalledTimes(1); + observerConstructor.mockRestore(); + }); });