Reactjs React门户能否用于无状态功能组件(SFC)?
我在有状态组件的呈现方法中使用了Reactjs React门户能否用于无状态功能组件(SFC)?,reactjs,Reactjs,我在有状态组件的呈现方法中使用了ReactDOM.createPortal,如下所示: class MyComponent extends Component { ... render() { return ( <Wrapper> {ReactDOM.createPortal(<FOO />, 'dom-location')} </Wrapper>
ReactDOM.createPortal
,如下所示:
class MyComponent extends Component {
...
render() {
return (
<Wrapper>
{ReactDOM.createPortal(<FOO />, 'dom-location')}
</Wrapper>
)
}
}
类MyComponent扩展组件{
...
render(){
返回(
{ReactDOM.createPortal(,'dom location')}
)
}
}
。。。但是它也可以被无状态(功能)组件使用吗?是的,根据主要要求是:
第一个参数(子参数)是任何可呈现的子参数,如元素、字符串或片段。第二个参数(容器)是DOM元素
在无状态组件的情况下,您可以通过道具传递元素,并通过门户呈现它
希望有帮助。对于固定组件,可以这样做:
const X = ({ children }) => ReactDOM.createPortal(children, 'dom-location')
const MyComponent = () => ReactDOM.createPortal(<FOO/>, 'dom-location')
无状态(功能)组件也可以使用它吗
?
对
内部渲染:
render() {
const modal = this.state.showModal ? (
<Modal>
<Hello/>
</Modal>
) : null;
return (
<div>
<div id="myEle">
</div>
</div>
);
}
render(){
const modal=this.state.showModal(
):null;
返回(
);
}
如果您不想手动更新index.html并添加额外的标记,则使用时会出现一个选项,此代码段将为您动态创建一个div,然后插入子项
export const Portal=({children,className='root Portal',el='div'})=>{
const[container]=React.useState(document.createElement(el))
container.classList.add(className)
React.useffect(()=>{
document.body.appendChild(容器)
return()=>{
document.body.removeChild(容器)
}
}, [])
返回ReactDOM.createPortal(子对象、容器)
}
SSR门户(NextJS)
如果您试图将上述任何一项用于SSR(例如NextJS),您可能会遇到困难
以下内容应该能满足您的需要。此方法允许传递用于门户的id/选择器,这在某些情况下可能会有所帮助,否则它将使用\uuuuuuuuroot\uuuportal\uuuuuu
创建默认值
如果找不到选择器,它将创建并附加一个div
注意:如果再次使用NextJS,还可以静态添加div并在页面/\u 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;
从'react'导入{PropsWithChildren,useffect,useState,useRef};
从'react dom'导入{createPortal};
导出接口IPortal{
选择器?:字符串;
}
const Portal=(props:PropsWithChildren)=>{
道具={
选择器:“\uuuu根目录\uuuu门户”,
…道具
};
常量{selector,children}=props;
const ref=useRef()
const[mounted,setMounted]=useState(false);
常量selectorPrefixed=“#”+选择器。替换(/^#/,”);
useffect(()=>{
ref.current=document.querySelector(selector或refix);
如果(!ref.current){
const div=document.createElement('div');
div.setAttribute('id',选择器);
文件.正文.附件(div);
参考电流=div;
}
设置已安装(真);
},[选择器];
返回装载?createPortal(子项,参考当前):空;
};
导出默认门户;
用法
下面是一个使用门户的快速示例。它没有考虑到位置等,只是简单地告诉你使用方法。天空是无限的:)
import React,{useState,cssprroperties}来自“React”;
将门户从“./path/导入到/Portal';//上面的路径
const modalStyle:csp属性={
填充:“3rem”,
背景颜色:“#eee”,
边距:“0自动”,
宽度:400
};
常量Home=()=>{
const[visible,setVisible]=useState(false);
返回(
你好,世界
{可见?你好模式!:null}
);
};
导出默认主页;
TSX版本基于@Samuel的回答(React 17,TS 4.1):
//portal.tsx
从“React”导入*作为React
从“react dom”导入*作为react dom
接口IProps{
类名?:字符串
el?:字符串
子项:React.ReactNode
}
/**
*基于https://stackoverflow.com/a/59154364
*@param children子元素
*@param className CSS className
*@param el要创建的HTML元素。默认值:div
*/
const Portal:React.FC=({children,className,el='div'}:IProps)=>{
const[container]=React.useState(document.createElement(el))
if(类名)
container.classList.add(className)
React.useffect(()=>{
document.body.appendChild(容器)
return()=>{
document.body.removeChild(容器)
}
}, [])
返回ReactDOM.createPortal(子对象、容器)
}
导出默认门户
是的,但正如@Denys Kotsur告诉我们的那样,将元素作为子元素通过道具创建是更好的方法。一行代码片段并不能提供完整的答案。对于任何给定的答案,详细阐述都是非常受欢迎的。我不知道这为什么没有得到更多的关注。这是一个真正动态的、放得好的解决方案,可在许多不同的场景中重用!
render() {
const modal = this.state.showModal ? (
<Modal>
<Hello/>
</Modal>
) : null;
return (
<div>
<div id="myEle">
</div>
</div>
);
}
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;
// 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