diff --git a/.gitignore b/.gitignore
index 5114de1e..8ca35990 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.storybook
*.iml
*.log
.idea
diff --git a/examples/portal.tsx b/examples/portal.tsx
new file mode 100644
index 00000000..3e92560c
--- /dev/null
+++ b/examples/portal.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import PortalWrapper from '../src/PortalWrapper';
+
+export default () => {
+ const divRef = React.useRef();
+
+ React.useEffect(() => {
+ console.log('>>>', divRef.current);
+ }, []);
+
+ return (
+ <>
+
+ {() => 2333
}
+
+ >
+ );
+};
diff --git a/package.json b/package.json
index b0314699..fda026fa 100644
--- a/package.json
+++ b/package.json
@@ -19,10 +19,8 @@
"url": "http://github.com/react-component/util/issues"
},
"license": "MIT",
- "config": {
- "port": 8100
- },
"scripts": {
+ "start": "cross-env NODE_ENV=development father doc dev --storybook",
"lint": "eslint src/ --ext .tsx,.ts & eslint tests/ --ext .js",
"compile": "father build",
"prepublishOnly": "npm run compile && np --yolo --no-publish",
@@ -38,6 +36,7 @@
"@umijs/fabric": "^2.0.8",
"coveralls": "^3.1.0",
"create-react-class": "^15.6.3",
+ "cross-env": "^7.0.2",
"enzyme": "^3.10.0",
"eslint": "^6.6.0",
"father": "^2.14.0",
diff --git a/src/Portal.js b/src/Portal.js
deleted file mode 100644
index 8a7bb50a..00000000
--- a/src/Portal.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-
-export default class Portal extends React.Component {
- componentDidMount() {
- this.createContainer();
- }
-
- componentDidUpdate(prevProps) {
- const { didUpdate } = this.props;
- if (didUpdate) {
- didUpdate(prevProps);
- }
- }
-
- componentWillUnmount() {
- this.removeContainer();
- }
-
- createContainer() {
- this._container = this.props.getContainer();
- this.forceUpdate();
- }
-
- removeContainer() {
- if (this._container) {
- this._container.parentNode.removeChild(this._container);
- }
- }
-
- render() {
- if (this._container) {
- return ReactDOM.createPortal(this.props.children, this._container);
- }
- return null;
- }
-}
diff --git a/src/Portal.tsx b/src/Portal.tsx
new file mode 100644
index 00000000..af1d9148
--- /dev/null
+++ b/src/Portal.tsx
@@ -0,0 +1,46 @@
+import * as React from 'react';
+import { useRef, forwardRef, useImperativeHandle } from 'react';
+import ReactDOM from 'react-dom';
+import canUseDom from './Dom/canUseDom';
+
+export type PortalRef = {};
+
+export interface PortalProps {
+ /** @deprecated Not know who use this? */
+ didUpdate?: (prevProps: PortalProps) => void;
+ getContainer: () => HTMLElement;
+ children?: React.ReactNode;
+}
+
+const Portal = forwardRef((props, ref) => {
+ const { didUpdate, getContainer, children } = props;
+
+ const containerRef = useRef();
+
+ // Ref return nothing, only for wrapper check exist
+ useImperativeHandle(ref, () => ({}));
+
+ // Create container in client side with sync to avoid useEffect not get ref
+ const initRef = useRef(false);
+ if (!initRef.current && canUseDom()) {
+ containerRef.current = getContainer();
+ initRef.current = true;
+ }
+
+ // Not know who use this. Just keep it here
+ React.useEffect(() => {
+ didUpdate?.(props);
+
+ return () => {
+ if (containerRef.current) {
+ containerRef.current.parentNode.removeChild(containerRef.current);
+ }
+ };
+ }, []);
+
+ return containerRef.current
+ ? ReactDOM.createPortal(children, containerRef.current)
+ : null;
+});
+
+export default Portal;
diff --git a/src/PortalWrapper.js b/src/PortalWrapper.tsx
similarity index 84%
rename from src/PortalWrapper.js
rename to src/PortalWrapper.tsx
index 27b1067d..f5193577 100644
--- a/src/PortalWrapper.js
+++ b/src/PortalWrapper.tsx
@@ -1,8 +1,8 @@
/* eslint-disable no-underscore-dangle,react/require-default-props */
-import React from 'react';
-import ReactDOM from 'react-dom';
+import * as React from 'react';
+import * as ReactDOM from 'react-dom';
import ContainerRender from './ContainerRender';
-import Portal from './Portal';
+import Portal, { PortalRef } from './Portal';
import switchScrollingEffect from './switchScrollingEffect';
import setStyle from './setStyle';
@@ -19,7 +19,7 @@ const IS_REACT_16 = 'createPortal' in ReactDOM;
// https://github.com/ant-design/ant-design/issues/19332
let cacheOverflow = {};
-const getParent = getContainer => {
+const getParent = (getContainer: GetContainer) => {
if (windowIsUndefined) {
return null;
}
@@ -40,8 +40,42 @@ const getParent = getContainer => {
return document.body;
};
-class PortalWrapper extends React.Component {
- constructor(props) {
+export type GetContainer = string | HTMLElement | (() => HTMLElement);
+
+export interface PortalWrapperProps {
+ visible?: boolean;
+ getContainer?: GetContainer;
+ wrapperClassName?: string;
+ forceRender?: boolean;
+ children: (info: {
+ getOpenCount: () => number;
+ getContainer: () => HTMLElement;
+ switchScrollingEffect: () => void;
+ ref?: (c: any) => void;
+ }) => React.ReactNode;
+}
+
+export interface PortalWrapperState {
+ _self: PortalWrapper;
+}
+
+class PortalWrapper extends React.Component<
+ PortalWrapperProps,
+ PortalWrapperState
+> {
+ container?: HTMLElement;
+
+ _component?: PortalRef;
+
+ renderComponent?: (info: {
+ afterClose: Function;
+ onClose: Function;
+ visible: boolean;
+ }) => void;
+
+ removeContainer?: Function;
+
+ constructor(props: PortalWrapperProps) {
super(props);
const { visible, getContainer } = props;
if (!windowIsUndefined && getParent(getContainer) === document.body) {
@@ -121,7 +155,7 @@ class PortalWrapper extends React.Component {
}
};
- savePortal = c => {
+ savePortal = (c: PortalRef) => {
// Warning: don't rename _component
// https://github.com/react-component/util/pull/65#discussion_r352407916
this._component = c;
@@ -175,7 +209,7 @@ class PortalWrapper extends React.Component {
getContainer: this.getContainer,
switchScrollingEffect: this.switchScrollingEffect,
};
- // suppport react15
+ // support react15
if (!IS_REACT_16) {
return (
{
+ let container: HTMLDivElement;
+
+ beforeEach(() => {
+ container = document.createElement('div');
+ document.body.appendChild(container);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(container);
+ });
+
+ it('forceRender', () => {
+ const divRef = React.createRef();
+
+ const wrapper = mount(
+
+ {() => 2333
}
+ ,
+ );
+
+ expect(divRef.current).toBeTruthy();
+
+ wrapper.unmount();
+ });
+});