Skip to content

Commit

Permalink
Merge f60a3db into 1082b46
Browse files Browse the repository at this point in the history
  • Loading branch information
zombieJ authored Aug 7, 2023
2 parents 1082b46 + f60a3db commit 68131bf
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 68 deletions.
55 changes: 55 additions & 0 deletions src/MutateObserver.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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';
import DomWrapper from './wrapper';
import type { MutationObserverProps } from './interface';
import useMutateObserver from './useMutateObserver';

const MutateObserver: React.FC<MutationObserverProps> = props => {
const { children, options, onMutate = () => {} } = props;

const callback = useEvent(onMutate);

const wrapperRef = React.useRef<DomWrapper>(null);

const elementRef = React.useRef<HTMLElement>(null);

const canRef = React.isValidElement(children) && supportRef(children);

const mergedRef = useComposeRef(
elementRef,
canRef ? (children as any).ref : null,
);

const [target, setTarget] = React.useState<HTMLElement>(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');

Check warning on line 41 in src/MutateObserver.tsx

View check run for this annotation

Codecov / codecov/patch

src/MutateObserver.tsx#L41

Added line #L41 was not covered by tests
}
return null;

Check warning on line 43 in src/MutateObserver.tsx

View check run for this annotation

Codecov / codecov/patch

src/MutateObserver.tsx#L43

Added line #L43 was not covered by tests
}

return (
<DomWrapper ref={wrapperRef}>
{canRef
? React.cloneElement(children as any, { ref: mergedRef })
: children}

Check warning on line 50 in src/MutateObserver.tsx

View check run for this annotation

Codecov / codecov/patch

src/MutateObserver.tsx#L50

Added line #L50 was not covered by tests
</DomWrapper>
);
};

export default MutateObserver;
71 changes: 3 additions & 68 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,6 @@
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';
import useMutateObserver from './useMutateObserver';

const defOptions: MutationObserverInit = {
subtree: true,
childList: true,
attributeFilter: ['style', 'class'],
};

const MutateObserver: React.FC<MutationObserverProps> = props => {
const { children, options = defOptions, onMutate = () => {} } = props;

const callback = useEvent(onMutate);

const wrapperRef = useRef<DomWrapper>(null);

const elementRef = React.useRef<HTMLElement>(null);

const canRef = React.isValidElement(children) && supportRef(children);

const originRef: React.Ref<Element> = canRef ? (children as any)?.ref : null;

const mergedRef = React.useMemo<React.Ref<Element>>(
() => composeRef<Element>(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 (
<DomWrapper ref={wrapperRef}>
{canRef
? React.cloneElement(children as any, { ref: mergedRef })
: children}
</DomWrapper>
);
};
export { useMutateObserver };

export default MutateObserver;
36 changes: 36 additions & 0 deletions src/useMutateObserver.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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(
nodeOrList: HTMLElement | HTMLElement[],
callback: MutationCallback,
options: MutationObserverInit = defaultOptions,
) {
React.useEffect(() => {
if (!canUseDom() || !nodeOrList) {
return;
}

let instance: MutationObserver;

const nodeList = Array.isArray(nodeOrList) ? nodeOrList : [nodeOrList];

if ('MutationObserver' in window) {
instance = new MutationObserver(callback);

Check warning on line 25 in src/useMutateObserver.tsx

View check run for this annotation

Codecov / codecov/patch

src/useMutateObserver.tsx#L25

Added line #L25 was not covered by tests

nodeList.forEach(element => {
instance.observe(element, options);

Check warning on line 28 in src/useMutateObserver.tsx

View check run for this annotation

Codecov / codecov/patch

src/useMutateObserver.tsx#L27-L28

Added lines #L27 - L28 were not covered by tests
});
}
return () => {
instance?.takeRecords();
instance?.disconnect();
};
}, [options, nodeOrList]);
}

0 comments on commit 68131bf

Please sign in to comment.