-
Notifications
You must be signed in to change notification settings - Fork 171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parent portal closes when clicking inside of a nested portal #191
Comments
In v3, portal component listen to mouseup and touchstart events, but in v4 it listen to click event, which not always works fine for me. https://github.com/tajo/react-portal/blob/3.x/lib/portal.js#L27 |
This can be fixed using event bubbling through the react-dom. The react doc has a chapter for this : https://reactjs.org/docs/portals.html#event-bubbling-through-portals I personally created my own portal wrapper like this : import uniqueId from 'lodash/uniqueId';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
class Portal extends Component {
componentDidMount() {
const { onClickOutside } = this.props;
if (!onClickOutside) return;
window.addEventListener('click', this.handleDocumentClick);
}
componentWillUnmount() {
if (this.defaultNode) {
document.body.removeChild(this.defaultNode);
}
this.defaultNode = null;
window.removeEventListener('click', this.handleDocumentClick);
}
id = this.props.id || uniqueId()
handleDocumentClick = (e) => {
if (!e.parentModals || !e.parentModals.includes(this.id)) {
this.props.onClickOutside(e);
}
}
handleClickInsideModal = (e) => {
const currentParentModals = e.nativeEvent.parentModals || [];
e.nativeEvent.parentModals = [
...currentParentModals,
this.id,
];
}
render() {
const { children } = this.props;
if (!this.defaultNode) {
this.defaultNode = document.createElement('div');
document.body.appendChild(this.defaultNode);
}
return ReactDOM.createPortal(
<div onClick={this.handleClickInsideModal}>
{children}
</div>,
this.defaultNode
);
}
}
Portal.propTypes = {
children: PropTypes.node,
onClickOutside: PropTypes.func,
id: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
])
};
export default Portal; The |
You can also nest portals in the DOM: import React from "react";
import ReactDOM from "react-dom";
const PortalContext = React.createContext(
typeof document !== "undefined" ? document.body : null
);
export function Portal({ children }) {
// if it's a nested portal, context is the parent portal
// otherwise it's document.body
const context = React.useContext(PortalContext);
const [container] = React.useState(() => {
if (typeof document !== "undefined") {
const portal = document.createElement("div");
portal.className = "portal";
return portal;
}
// ssr
return null;
});
React.useLayoutEffect(() => {
if (container && context) {
context.appendChild(container);
return () => {
context.removeChild(container);
};
}
return undefined;
}, [container, context]);
if (container) {
const portal = ReactDOM.createPortal(children, container);
return (
<PortalContext.Provider value={container}>
{portal}
</PortalContext.Provider>
);
}
// ssr
return null;
} |
This issue was originally discussed in #101, before the rewrite.
I created a bin reproducing it with the latest version of react-portal:
https://www.webpackbin.com/bins/-L2PNDWSpoEmiOUuzvFO
The text was updated successfully, but these errors were encountered: