我在有状态组件的render方法中使用了ReactDOM.createPortal
,如下所示:
class MyComponent extends Component {
...
render() {
return (
<Wrapper>
{ReactDOM.createPortal(<FOO />, 'dom-location')}
</Wrapper>
)
}
}
...但也可以由无状态(功能)组件使用吗?
答案 0 :(得分:2)
对于固定组件,可以这样做:
const MyComponent = () => ReactDOM.createPortal(<FOO/>, 'dom-location')
或者,为了使函数更灵活,通过传递component
道具:
const MyComponent = ({ component }) => ReactDOM.createPortal(component, 'dom-location')
答案 1 :(得分:1)
是的,根据docs,主要要求是:
第一个参数(child)是任何可渲染的React子元素,例如元素,字符串或片段。第二个参数(容器)是一个DOM元素。
如果是无状态组件,您可以通过props传递元素并通过门户网站呈现它。
希望它会有所帮助。
答案 2 :(得分:1)
const X = ({ children }) => ReactDOM.createPortal(children, 'dom-location')
答案 3 :(得分:1)
如果您尝试将上述任何一项与 SSR(例如 NextJS)一起使用,您可能会遇到困难。
以下内容应该可以满足您的需求。此方法允许传入 ID/选择器以用于门户,这在某些情况下会很有帮助,否则它会使用 __ROOT_PORTAL__
创建默认值。
如果找不到选择器,它将创建并附加一个 div。
注意:如果再次使用 NextJS,您还可以静态添加一个 div 并在 pages/_document.tsx
(或 .jsx)中指定一个已知 ID。传入那个 id,它会尝试找到并使用它。
import { PropsWithChildren, useEffect, useState, useRef } from 'react';
import { createPortal } from 'react-dom';
export interface IPortal {
selector?: string;
}
const Portal = (props: PropsWithChildren<IPortal>) => {
props = {
selector: '__ROOT_PORTAL__',
...props
};
const { selector, children } = props;
const ref = useRef<Element>()
const [mounted, setMounted] = useState(false);
const selectorPrefixed = '#' + selector.replace(/^#/, '');
useEffect(() => {
ref.current = document.querySelector(selectorPrefixed);
if (!ref.current) {
const div = document.createElement('div');
div.setAttribute('id', selector);
document.body.appendChild(div);
ref.current = div;
}
setMounted(true);
}, [selector]);
return mounted ? createPortal(children, ref.current) : null;
};
export default Portal;
以下是使用门户的快速示例。它不考虑位置等。只是简单地向您展示用法。天空是有限的:)
import React, { useState, CSSProperties } from 'react';
import Portal from './path/to/portal'; // Path to above
const modalStyle: CSSProperties = {
padding: '3rem',
backgroundColor: '#eee',
margin: '0 auto',
width: 400
};
const Home = () => {
const [visible, setVisible] = useState(false);
return (
<>
<p>Hello World <a href="#" onClick={() => setVisible(true)}>Show Modal</a></p>
<Portal>
{visible ? <div style={modalStyle}>Hello Modal! <a href="#" onClick={() => setVisible(false)}>Close</a></div> : null}
</Portal>
</>
);
};
export default Home;
答案 4 :(得分:0)
也可以由无状态(功能)组件使用 吗
是
new Microsoft.Maps.Map('#react-bingmaps')
内部渲染:
Microsoft' is not defined
答案 5 :(得分:0)
您可以选择不希望手动更新index.html并添加额外标记的选项,此代码段将为您动态创建div,然后插入子代。
export const Portal = ({ children, className = 'root-portal', el = 'div' }) => {
const [container] = React.useState(document.createElement(el))
container.classList.add(className)
React.useEffect(() => {
document.body.appendChild(container)
return () => {
document.body.removeChild(container)
}
}, [])
return ReactDOM.createPortal(children, container)
}
答案 6 :(得分:0)
基于@Samuel 的回答的 TSX 版本(React 17,TS 4.1):
// portal.tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
interface IProps {
className? : string
el? : string
children : React.ReactNode
}
/**
* React portal based on https://stackoverflow.com/a/59154364
* @param children Child elements
* @param className CSS classname
* @param el HTML element to create. default: div
*/
const Portal : React.FC<IProps> = ( { children, className, el = 'div' } : IProps ) => {
const [container] = React.useState(document.createElement(el))
if ( className )
container.classList.add(className)
React.useEffect(() => {
document.body.appendChild(container)
return () => {
document.body.removeChild(container)
}
}, [])
return ReactDOM.createPortal(children, container)
}
export default Portal