Вы действительно знаете React Portal?

React.js

Автор: markzzw Время: 2019-12-19 Оригинальный адрес:исходный адрес

Введение

Знаете ли вы предшественника настоящего и реагирующего портала на него? Эта статья расскажет вам ответ.

содержание

  1. Что такое Portal и оригинал
  2. Реагировать портал сейчас
  3. Предыдущий портал React
  4. Сценарии использования портала
  5. использованная литература

Что такое портал

PortalПредоставляет отличное решение для рендеринга дочерних узлов в узлы DOM, которые существуют вне родительского компонента. ОдинportalТипичный вариант использования — когда родительский компонент имеетoverflow: hiddenилиz-indexпри стилизации, но вам нужно, чтобы дочерний компонент мог визуально «выпрыгивать» из своего контейнера. Например, диалоги, всплывающие подсказки и всплывающие подсказки.

ВышеReactизофициальное объяснение

с картинками

配图

React-портал сейчас

существуетReact16Позже,Reactобеспечивает функциюReactDOM.createPortal(child, container)сделатьReact 子元素к указанномуcontainer, очень удобен в использовании;

Модальный пример с официального сайта React

Это уже должно быть известноportalиспользование

const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    );
  }
}

Предыдущий React-портал

Затем вReactне предоставленcreatePortal()когда,PortalКак этого добиться? Здесь мы будем использовать другой методReactDOM.render(element, container[, callback]);

Пример использования ReactDOM.render

interface PortalProps extends CommonComponentProps {
  getContainer?: Function;
  visible?: boolean;
}

class Portal extends React.Component<PortalProps> {
  mountDom: HTMLElement;
  container: HTMLElement;
  constructor(props:PortalProps) {
    super(props);
    this.mountDom = document.createElement('div');
    this.container = props.getContainer
    ? props.getContainer()
    : window.document.body;
  }

  componentDidMount() {
    this.container.appendChild(this.mountDom);
  }

  componentWillUnmount() {
    if (this.mountDom) {
      this.container.removeChild(this.mountDom);
    }
  }

  getVisible = () => {
    if ('visible' in this.props) {
      return this.props.visible;
    }
    return true;
  }

  render() {
    const { children } = this.props;
    if (this.container && this.getVisible()) {
      if (createPortal) {
        return createPortal(children, this.mountDom);
      }
      ReactDom.render(children, this.mountDom);
    }
    return null;
  }
}

export default Portal;

даже в некоторыхкодтакже появляется при использованииReactDOM.unstable_renderSubtreeIntoContainer(paren,component,container,callback);Это вcreatePortalКогда решение не появляется, некоторые решения;

Сценарии использования портала

Наиболее известным является созданиеModalилиDialogкомпонент;

export interface DialogWrapperProps extends DialogProps {
  visible?: boolean;
  getContainer?: () => void;
}

class DialogWrapper extends Component<DialogWrapperProps>{
  render() {
    const { getContainer, children, onClose, visible } = this.props;
    if (visible) {
      return (
        <Portal
          visible={visible}
          getContainer={getContainer}
        >
          <Dialog
            onClose={onClose}
          >
            {children}
          </Dialog>
        </Portal>
      );
    }
    return null;
  }
}

export default DialogWrapper;

При создании выпадающего меню Dropdown

Потому что вы можете монтировать необходимые файлы на любом узлеReact 元素, поэтому нам также нужно будет использовать его в раскрывающемся компонентеPortalпомогите нам сделать это;

class Trigger extends Component<TriggerProps, {
  triggerVisible: boolean
}> {
  popupContainer: HTMLElement;
  node: any;
  popupRef: any;
  clickPopupOutSideFun: void | null;

  constructor(props: TriggerProps) {
  }

  getPortalContainer = () => {...};

  creatPopupContainer = () => {...}

  getContainer = () => {...};

  render() {
    const {
      children,
      className
    } = this.props;

    const {
      triggerVisible
    } = this.state;

    const trigger = React.cloneElement(children, {
      className: classNames('pb-dropdown-trigger', className),
      ref: composeRef(this.node, (children as any).ref) // compose this component ref and children refs
    });

    let portal: React.ReactElement | null = null;
    if (triggerVisible) {
      portal = (
        <Portal
          key="portal"
          getContainer={this.getContainer}
        >
          {this.getPortalContainer()}
        </Portal>
      );
    }

    const newChildProps: HTMLAttributes<HTMLElement> & { key: string } = { key: 'trigger' };
    const child = (
      <div>
        {trigger}
        {portal}
      </div>
    );

    this.genNewChildren(newChildProps);
    
    const newChild = React.cloneElement(child, {
      ...newChildProps,
      ref: composeRef(this.node, (children as any).ref)
    });
    return newChild;
  }
}

export default Trigger;

использованная литература

  1. react portal
  2. ReactDom.render
  3. antd portal
  4. Пример кода портала в этой статье
  5. Пример кода раскрывающегося списка в этой статье