Skip to content

Commit efa0dee

Browse files
authored
chore: support ref (#5)
1 parent 198c70e commit efa0dee

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"test": "npm run test:client && npm run test:server",
4040
"test:client": "umi-test --testPathIgnorePatterns=ssr.test.tsx --testPathIgnorePatterns=ssr.test.tsx",
4141
"test:coverage": "npm run test:client --coverage",
42-
"test:server": "umi-test --env=node tests/ssr.test.tsx",
42+
"test:server": "umi-test --env=node -- tests/ssr.test.tsx",
4343
"watch": "father dev"
4444
},
4545
"dependencies": {

src/Portal.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
22
import { createPortal } from 'react-dom';
33
import canUseDom from 'rc-util/lib/Dom/canUseDom';
4+
import { supportRef, useComposeRef } from 'rc-util/lib/ref';
45
import OrderContext from './Context';
56
import useDom from './useDom';
67
import useScrollLocker from './useScrollLocker';
@@ -47,7 +48,7 @@ const getPortalContainer = (getContainer: GetContainer) => {
4748
return getContainer;
4849
};
4950

50-
export default function Portal(props: PortalProps) {
51+
const Portal = React.forwardRef<any, PortalProps>((props, ref) => {
5152
const {
5253
open,
5354
autoLock,
@@ -93,6 +94,15 @@ export default function Portal(props: PortalProps) {
9394
mergedContainer === document.body),
9495
);
9596

97+
// =========================== Ref ===========================
98+
let childRef: React.Ref<any> = null;
99+
100+
if (children && supportRef(children) && ref) {
101+
({ ref: childRef } = children as any);
102+
}
103+
104+
const mergedRef = useComposeRef(childRef, ref);
105+
96106
// ========================= Render ==========================
97107
// Do not render when nothing need render
98108
// When innerContainer is `undefined`, it may not ready since user use ref in the same render
@@ -103,9 +113,24 @@ export default function Portal(props: PortalProps) {
103113
// Render inline
104114
const renderInline = mergedContainer === false || inlineMock();
105115

116+
let reffedChildren = children;
117+
if (ref) {
118+
reffedChildren = React.cloneElement(children as any, {
119+
ref: mergedRef,
120+
});
121+
}
122+
106123
return (
107124
<OrderContext.Provider value={queueCreate}>
108-
{renderInline ? children : createPortal(children, mergedContainer)}
125+
{renderInline
126+
? reffedChildren
127+
: createPortal(reffedChildren, mergedContainer)}
109128
</OrderContext.Provider>
110129
);
130+
});
131+
132+
if (process.env.NODE_ENV !== 'production') {
133+
Portal.displayName = 'Portal';
111134
}
135+
136+
export default Portal;

tests/index.test.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,52 @@ describe('Portal', () => {
172172
rerender(renderDemo(false, true));
173173
expect(document.querySelector('p')).toBeFalsy();
174174
});
175+
176+
describe.only('ref-able', () => {
177+
it('support forwardRef', () => {
178+
const elementRef = React.createRef<HTMLParagraphElement>();
179+
const portalRef = React.createRef();
180+
181+
render(
182+
<Portal ref={portalRef} open>
183+
<p ref={elementRef}>Bamboo</p>
184+
</Portal>,
185+
);
186+
187+
expect(elementRef.current).toBe(document.querySelector('p'));
188+
expect(portalRef.current).toBe(document.querySelector('p'));
189+
});
190+
191+
it('not support fragment', () => {
192+
const elementRef = React.createRef<HTMLParagraphElement>();
193+
const portalRef = React.createRef();
194+
195+
render(
196+
<Portal ref={portalRef} open>
197+
<>
198+
<p ref={elementRef}>Bamboo</p>
199+
</>
200+
</Portal>,
201+
);
202+
203+
expect(elementRef.current).toBe(document.querySelector('p'));
204+
expect(portalRef.current).toBeFalsy();
205+
});
206+
207+
it('not support FC', () => {
208+
const elementRef = React.createRef<HTMLParagraphElement>();
209+
const portalRef = React.createRef();
210+
211+
const P = (props: any) => <p {...props} />;
212+
213+
render(
214+
<Portal ref={portalRef} open>
215+
<P ref={elementRef}>Bamboo</P>
216+
</Portal>,
217+
);
218+
219+
expect(elementRef.current).toBeFalsy();
220+
expect(portalRef.current).toBeFalsy();
221+
});
222+
});
175223
});

0 commit comments

Comments
 (0)