Skip to content

Commit 9be84bb

Browse files
authored
feat: support renderProps (#74)
1 parent fc029a4 commit 9be84bb

File tree

5 files changed

+67
-7
lines changed

5 files changed

+67
-7
lines changed

docs/demo/renderProps.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## renderProps
2+
3+
<code src="../../examples/renderProps.tsx">

examples/renderProps.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import '../assets/index.less';
2+
import React from 'react';
3+
import type { ResizeObserverProps } from '../src';
4+
import ResizeObserver from '../src';
5+
6+
export default function App() {
7+
const [times, setTimes] = React.useState(0);
8+
const [disabled, setDisabled] = React.useState(false);
9+
10+
const onResize: ResizeObserverProps['onResize'] = () => {
11+
setTimes(prevTimes => prevTimes + 1);
12+
};
13+
14+
return (
15+
<React.StrictMode>
16+
<div style={{ transform: 'scale(1.1)', transformOrigin: '0% 0%' }}>
17+
<div>
18+
<label>
19+
<input
20+
type="checkbox"
21+
onChange={() => {
22+
setDisabled(!disabled);
23+
}}
24+
checked={disabled}
25+
/>{' '}
26+
Disabled Observe
27+
</label>
28+
{' >>> '}
29+
<span>Resize times: {times}</span>
30+
</div>
31+
<ResizeObserver onResize={onResize} disabled={disabled}>
32+
{resizeRef => (
33+
<div style={{ display: 'inline-flex', flexDirection: 'column' }}>
34+
<textarea placeholder="I'm a textarea!" />
35+
<div ref={resizeRef} style={{ background: 'red', height: 50, fontSize: 10 }}>
36+
Target
37+
</div>
38+
</div>
39+
)}
40+
</ResizeObserver>
41+
</div>
42+
</React.StrictMode>
43+
);
44+
}

src/SingleObserver/index.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import DomWrapper from './DomWrapper';
77
import { CollectionContext } from '../Collection';
88

99
export interface SingleObserverProps extends ResizeObserverProps {
10-
children: React.ReactElement;
10+
children: React.ReactElement | ((ref: React.RefObject<Element>) => React.ReactElement);
1111
}
1212

1313
export default function SingleObserver(props: SingleObserverProps) {
@@ -17,6 +17,10 @@ export default function SingleObserver(props: SingleObserverProps) {
1717

1818
const onCollectionResize = React.useContext(CollectionContext);
1919

20+
// =========================== Children ===========================
21+
const isRenderProps = typeof children === 'function';
22+
const mergedChildren = isRenderProps ? children(elementRef) : children;
23+
2024
// ============================= Size =============================
2125
const sizeRef = React.useRef({
2226
width: -1,
@@ -26,8 +30,9 @@ export default function SingleObserver(props: SingleObserverProps) {
2630
});
2731

2832
// ============================= Ref ==============================
29-
const canRef = React.isValidElement(children) && supportRef(children);
30-
const originRef: React.Ref<Element> = canRef ? (children as any).ref : null;
33+
const canRef =
34+
!isRenderProps && React.isValidElement(mergedChildren) && supportRef(mergedChildren);
35+
const originRef: React.Ref<Element> = canRef ? (mergedChildren as any).ref : null;
3136

3237
const mergedRef = React.useMemo(
3338
() => composeRef<Element>(originRef, elementRef),
@@ -100,10 +105,10 @@ export default function SingleObserver(props: SingleObserverProps) {
100105
return (
101106
<DomWrapper ref={wrapperRef}>
102107
{canRef
103-
? React.cloneElement(children as any, {
108+
? React.cloneElement(mergedChildren as any, {
104109
ref: mergedRef,
105110
})
106-
: children}
111+
: mergedChildren}
107112
</DomWrapper>
108113
);
109114
}

src/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ export type OnResize = (size: SizeInfo, element: HTMLElement) => void;
1818
export interface ResizeObserverProps {
1919
/** Pass to ResizeObserver.Collection with additional data */
2020
data?: any;
21-
children: React.ReactNode;
21+
children: React.ReactNode | ((ref: React.RefObject<any>) => React.ReactElement);
2222
disabled?: boolean;
2323
/** Trigger if element resized. Will always trigger when first time render. */
2424
onResize?: OnResize;
2525
}
2626

2727
function ResizeObserver(props: ResizeObserverProps) {
2828
const { children } = props;
29-
const childNodes = toArray(children);
29+
const childNodes = typeof children === 'function' ? [children] : toArray(children);
3030

3131
if (process.env.NODE_ENV !== 'production') {
3232
if (childNodes.length > 1) {

tests/index.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,12 @@ describe('ResizeObserver', () => {
237237

238238
expect(onResize).toHaveBeenCalled();
239239
});
240+
241+
it('support renderProps', () => {
242+
const wrapper = mount(
243+
<ResizeObserver>{ref => <div ref={ref} className="block" />}</ResizeObserver>,
244+
);
245+
246+
expect(wrapper.exists('.block')).toBeTruthy();
247+
});
240248
});

0 commit comments

Comments
 (0)