From 085d1bcccf6aeda6164522074021af27793cee20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Mon, 7 Aug 2023 19:07:02 +0800 Subject: [PATCH 1/4] chore: move --- src/MutateObserver.tsx | 71 +++++++++++++++++++++++++++++++++++++++++ src/index.tsx | 72 ++---------------------------------------- 2 files changed, 73 insertions(+), 70 deletions(-) create mode 100644 src/MutateObserver.tsx diff --git a/src/MutateObserver.tsx b/src/MutateObserver.tsx new file mode 100644 index 0000000..c7bd569 --- /dev/null +++ b/src/MutateObserver.tsx @@ -0,0 +1,71 @@ +import React, { useEffect, useRef } from 'react'; +import { composeRef, supportRef } from 'rc-util/lib/ref'; +import findDOMNode from 'rc-util/lib/Dom/findDOMNode'; +import canUseDom from 'rc-util/lib/Dom/canUseDom'; +import useEvent from 'rc-util/lib/hooks/useEvent'; +import DomWrapper from './wrapper'; +import type { MutationObserverProps } from './interface'; + +const defOptions: MutationObserverInit = { + subtree: true, + childList: true, + attributeFilter: ['style', 'class'], +}; + +const MutateObserver: React.FC = props => { + const { children, options = defOptions, onMutate = () => {} } = props; + + const callback = useEvent(onMutate); + + const wrapperRef = useRef(null); + + const elementRef = React.useRef(null); + + const canRef = React.isValidElement(children) && supportRef(children); + + const originRef: React.Ref = canRef ? (children as any)?.ref : null; + + const mergedRef = React.useMemo>( + () => composeRef(originRef, elementRef), + [originRef, elementRef], + ); + + useEffect(() => { + if (!canUseDom()) { + return; + } + + let instance: MutationObserver; + + const currentElement = + findDOMNode((originRef as any)?.current) || + findDOMNode(wrapperRef?.current); + + if (currentElement && 'MutationObserver' in window) { + instance = new MutationObserver(callback); + instance.observe(currentElement, options); + } + return () => { + instance?.takeRecords(); + instance?.disconnect(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [options, originRef]); + + if (!children) { + if (process.env.NODE_ENV !== 'production') { + console.error('MutationObserver need children props'); + } + return null; + } + + return ( + + {canRef + ? React.cloneElement(children as any, { ref: mergedRef }) + : children} + + ); +}; + +export default MutateObserver; diff --git a/src/index.tsx b/src/index.tsx index c7bd569..3d92f52 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,71 +1,3 @@ -import React, { useEffect, useRef } from 'react'; -import { composeRef, supportRef } from 'rc-util/lib/ref'; -import findDOMNode from 'rc-util/lib/Dom/findDOMNode'; -import canUseDom from 'rc-util/lib/Dom/canUseDom'; -import useEvent from 'rc-util/lib/hooks/useEvent'; -import DomWrapper from './wrapper'; -import type { MutationObserverProps } from './interface'; +import MutateObserver from './MutateObserver' -const defOptions: MutationObserverInit = { - subtree: true, - childList: true, - attributeFilter: ['style', 'class'], -}; - -const MutateObserver: React.FC = props => { - const { children, options = defOptions, onMutate = () => {} } = props; - - const callback = useEvent(onMutate); - - const wrapperRef = useRef(null); - - const elementRef = React.useRef(null); - - const canRef = React.isValidElement(children) && supportRef(children); - - const originRef: React.Ref = canRef ? (children as any)?.ref : null; - - const mergedRef = React.useMemo>( - () => composeRef(originRef, elementRef), - [originRef, elementRef], - ); - - useEffect(() => { - if (!canUseDom()) { - return; - } - - let instance: MutationObserver; - - const currentElement = - findDOMNode((originRef as any)?.current) || - findDOMNode(wrapperRef?.current); - - if (currentElement && 'MutationObserver' in window) { - instance = new MutationObserver(callback); - instance.observe(currentElement, options); - } - return () => { - instance?.takeRecords(); - instance?.disconnect(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [options, originRef]); - - if (!children) { - if (process.env.NODE_ENV !== 'production') { - console.error('MutationObserver need children props'); - } - return null; - } - - return ( - - {canRef - ? React.cloneElement(children as any, { ref: mergedRef }) - : children} - - ); -}; - -export default MutateObserver; +export default MutateObserver; \ No newline at end of file From cd857f39f133f877a59e93e57036800b2bba4a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Mon, 7 Aug 2023 19:20:05 +0800 Subject: [PATCH 2/4] chore: fix export --- src/MutateObserver.tsx | 50 ++++++++++----------------------------- src/index.tsx | 7 ++++-- src/useMutateObserver.tsx | 34 ++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 39 deletions(-) create mode 100644 src/useMutateObserver.tsx diff --git a/src/MutateObserver.tsx b/src/MutateObserver.tsx index c7bd569..0505b99 100644 --- a/src/MutateObserver.tsx +++ b/src/MutateObserver.tsx @@ -1,56 +1,32 @@ -import React, { useEffect, useRef } from 'react'; -import { composeRef, supportRef } from 'rc-util/lib/ref'; +import React from 'react'; +import { supportRef, useComposeRef } from 'rc-util/lib/ref'; import findDOMNode from 'rc-util/lib/Dom/findDOMNode'; -import canUseDom from 'rc-util/lib/Dom/canUseDom'; import useEvent from 'rc-util/lib/hooks/useEvent'; import DomWrapper from './wrapper'; import type { MutationObserverProps } from './interface'; - -const defOptions: MutationObserverInit = { - subtree: true, - childList: true, - attributeFilter: ['style', 'class'], -}; +import useMutateObserver from './useMutateObserver'; const MutateObserver: React.FC = props => { - const { children, options = defOptions, onMutate = () => {} } = props; + const { children, options, onMutate = () => {} } = props; const callback = useEvent(onMutate); - const wrapperRef = useRef(null); + const wrapperRef = React.useRef(null); const elementRef = React.useRef(null); const canRef = React.isValidElement(children) && supportRef(children); - const originRef: React.Ref = canRef ? (children as any)?.ref : null; - - const mergedRef = React.useMemo>( - () => composeRef(originRef, elementRef), - [originRef, elementRef], + const mergedRef = useComposeRef( + elementRef, + canRef ? (children as any).ref : null, ); - useEffect(() => { - if (!canUseDom()) { - return; - } - - let instance: MutationObserver; - - const currentElement = - findDOMNode((originRef as any)?.current) || - findDOMNode(wrapperRef?.current); - - if (currentElement && 'MutationObserver' in window) { - instance = new MutationObserver(callback); - instance.observe(currentElement, options); - } - return () => { - instance?.takeRecords(); - instance?.disconnect(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [options, originRef]); + useMutateObserver( + () => findDOMNode(elementRef.current) || findDOMNode(wrapperRef.current), + callback, + options, + ); if (!children) { if (process.env.NODE_ENV !== 'production') { diff --git a/src/index.tsx b/src/index.tsx index 3d92f52..e3d5dad 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,3 +1,6 @@ -import MutateObserver from './MutateObserver' +import MutateObserver from './MutateObserver'; +import useMutateObserver from './useMutateObserver'; -export default MutateObserver; \ No newline at end of file +export { useMutateObserver }; + +export default MutateObserver; diff --git a/src/useMutateObserver.tsx b/src/useMutateObserver.tsx new file mode 100644 index 0000000..0d255dc --- /dev/null +++ b/src/useMutateObserver.tsx @@ -0,0 +1,34 @@ +import canUseDom from 'rc-util/lib/Dom/canUseDom'; +import * as React from 'react'; + +const defaultOptions: MutationObserverInit = { + subtree: true, + childList: true, + attributeFilter: ['style', 'class'], +}; + +export default function useMutateObserver( + getNode: () => HTMLElement, + callback: MutationCallback, + options: MutationObserverInit = defaultOptions, +) { + React.useEffect(() => { + if (!canUseDom()) { + return; + } + + let instance: MutationObserver; + + const currentElement = getNode(); + + if (currentElement && 'MutationObserver' in window) { + instance = new MutationObserver(callback); + instance.observe(currentElement, options); + } + return () => { + instance?.takeRecords(); + instance?.disconnect(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [options]); +} From b6f7f331b529fff217743eb3aea141ccfdacd59a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Mon, 7 Aug 2023 19:29:50 +0800 Subject: [PATCH 3/4] chore: refactor --- src/MutateObserver.tsx | 18 +++++++++++++----- src/useMutateObserver.tsx | 13 ++++++++----- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/MutateObserver.tsx b/src/MutateObserver.tsx index 0505b99..435ed45 100644 --- a/src/MutateObserver.tsx +++ b/src/MutateObserver.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; import { supportRef, useComposeRef } from 'rc-util/lib/ref'; import findDOMNode from 'rc-util/lib/Dom/findDOMNode'; import useEvent from 'rc-util/lib/hooks/useEvent'; @@ -22,12 +23,19 @@ const MutateObserver: React.FC = props => { canRef ? (children as any).ref : null, ); - useMutateObserver( - () => findDOMNode(elementRef.current) || findDOMNode(wrapperRef.current), - callback, - options, - ); + const [target, setTarget] = React.useState(null); + + useMutateObserver(target, callback, options); + + // =========================== Effect =========================== + // Bind target + useLayoutEffect(() => { + setTarget( + findDOMNode(elementRef.current) || findDOMNode(wrapperRef.current), + ); + }); + // =========================== Render =========================== if (!children) { if (process.env.NODE_ENV !== 'production') { console.error('MutationObserver need children props'); diff --git a/src/useMutateObserver.tsx b/src/useMutateObserver.tsx index 0d255dc..bba8076 100644 --- a/src/useMutateObserver.tsx +++ b/src/useMutateObserver.tsx @@ -8,22 +8,25 @@ const defaultOptions: MutationObserverInit = { }; export default function useMutateObserver( - getNode: () => HTMLElement, + nodeOrList: HTMLElement | HTMLElement[], callback: MutationCallback, options: MutationObserverInit = defaultOptions, ) { React.useEffect(() => { - if (!canUseDom()) { + if (!canUseDom() || !nodeOrList) { return; } let instance: MutationObserver; - const currentElement = getNode(); + const nodeList = Array.isArray(nodeOrList) ? nodeOrList : [nodeOrList]; - if (currentElement && 'MutationObserver' in window) { + if ('MutationObserver' in window) { instance = new MutationObserver(callback); - instance.observe(currentElement, options); + + nodeList.forEach(element => { + instance.observe(element, options); + }); } return () => { instance?.takeRecords(); From f60a3db90a28ddf263eaa80285309e414d0e8bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Mon, 7 Aug 2023 19:35:53 +0800 Subject: [PATCH 4/4] chore: update effect deps --- src/useMutateObserver.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/useMutateObserver.tsx b/src/useMutateObserver.tsx index bba8076..4c31d1d 100644 --- a/src/useMutateObserver.tsx +++ b/src/useMutateObserver.tsx @@ -32,6 +32,5 @@ export default function useMutateObserver( instance?.takeRecords(); instance?.disconnect(); }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [options]); + }, [options, nodeOrList]); }