可以在无状态功能组件(SFC)中使用React门户吗?

时间:2018-03-22 10:37:22

标签: reactjs

我在有状态组件的render方法中使用了ReactDOM.createPortal,如下所示:

class MyComponent extends Component {
    ...
    render() {
        return (
            <Wrapper>
                {ReactDOM.createPortal(<FOO />, 'dom-location')}
            </Wrapper>
        )
    }
}

...但也可以由无状态(功能)组件使用吗?

7 个答案:

答案 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) 的门户

如果您尝试将上述任何一项与 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

工作codesandbox#demo

答案 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