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

IT技术 reactjs
2021-05-20 23:09:54

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

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

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

4个回答

将加入一个您不想手动更新 index.html 并添加额外标记的选项,此代码段将为您动态创建一个 div,然后插入子项。

export const Portal = ({ children, className = 'root-portal', el = 'div' }) => {
  const [container] = React.useState(() => {
    // This will be executed only on the initial render
    // https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
    return document.createElement(el);
  });

  React.useEffect(() => {
    container.classList.add(className)
    document.body.appendChild(container)
    return () => {
      document.body.removeChild(container)
    }
  }, [])

  return ReactDOM.createPortal(children, container)
}

对于固定组件,可以这样做:

const MyComponent = () => ReactDOM.createPortal(<FOO/>, 'dom-location')

或者,为了使函数更灵活,通过传递一个componentprop:

const MyComponent = ({ component }) => ReactDOM.createPortal(component, 'dom-location')

它也可以被无状态(功能)组件使用吗?

是的。

const Modal = (props) => {
      const modalRoot = document.getElementById('myEle');

      return ReactDOM.createPortal(props.children, modalRoot,);
    }

内部渲染:

render() {
    const modal = this.state.showModal ? (
      <Modal>
        <Hello/>
        </Modal>
    ) : null;

    return (
      <div>
      <div id="myEle">
        </div>
      </div>
    );
  }

在职的 codesandbox#demo

TSX 版本基于@Samuel 的回答(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