Spec-Zone.ru › React 17

Порталы

Порталы обеспечивают первоклассный способ отображения дочерних элементов в узел DOM, который существует вне иерархии DOM родительского компонента.

ReactDOM.createPortal(child, container)

Первый аргумент (child) — любой отображаемый React-дочерний элемент, такой как элемент, строка или фрагмент. Второй аргумент (container) — DOM-элемент.

Использование

Обычно, когда вы возвращаете элемент из метода рендеринга компонента, он монтируется в DOM как дочерний элемент ближайшего родительского узла:

render() {
  // React mounts a new div and renders the children into it
  return (
    <div>
      {this.props.children}
    </div>
  );
}

Однако иногда бывает полезно вставить дочерний элемент в другое место в DOM:

render() {
  // React does *not* create a new div. It renders the children into `domNode`.
  // `domNode` is any valid DOM node, regardless of its location in the DOM.
  return ReactDOM.createPortal(
    this.props.children,
    domNode
  );
}

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

Примечание:

Работая с порталами, помните, что управление фокусом клавиатуры становится очень важным.

Для модальных диалогов убедитесь, что каждый может с ними взаимодействовать, следуя практическим рекомендациям по разработке модальных диалогов WAI-ARIA.

Попробовать на CodePen

Передача событий через порталы

Несмотря на то, что портал может находиться в любом месте дерева DOM, он ведет себя как обычный React-дочерний элемент во всех других отношениях. Функции, такие как контекст, работают точно так же, независимо от того, является ли дочерний элемент порталом, так как портал все равно существует в React-дереве, независимо от положения в DOM-дереве.

Это включает в себя передачу событий. Событие, сгенерированное внутри портала, будет распространяться на предков в содержащем React-дереве, даже если эти элементы не являются предками в DOM-дереве. Предполагая следующую структуру HTML:

<html>
  <body>
    <div id="app-root"></div>
    <div id="modal-root"></div>
  </body>
</html>

Компонент Parent в #app-root смог бы перехватить неперехваченное, распространяемое событие от соседнего узла #modal-root.

// These two containers are siblings in the DOM
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

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

  componentDidMount() {
    // The portal element is inserted in the DOM tree after
    // the Modal's children are mounted, meaning that children
    // will be mounted on a detached DOM node. If a child
    // component requires to be attached to the DOM tree
    // immediately when mounted, for example to measure a
    // DOM node, or uses 'autoFocus' in a descendant, add
    // state to Modal and only render the children when Modal
    // is inserted in the DOM tree.
    modalRoot.appendChild(this.el);
  }

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

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

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks: 0};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // This will fire when the button in Child is clicked,
    // updating Parent's state, even though button
    // is not direct descendant in the DOM.
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        <p>Number of clicks: {this.state.clicks}</p>
        <p>
          Open up the browser DevTools
          to observe that the button
          is not a child of the div
          with the onClick handler.
        </p>
        <Modal>
          <Child />
        </Modal>
      </div>
    );
  }
}

function Child() {
  // The click event on this button will bubble up to parent,
  // because there is no 'onClick' attribute defined
  return (
    <div className="modal">
      <button>Click</button>
    </div>
  );
}

ReactDOM.render(<Parent />, appRoot);

Попробовать на CodePen

Перехват события, распространяющегося вверх из портала в родительском компоненте, позволяет разрабатывать более гибкие абстракции, которые не зависят от порталов по своей природе. Например, если вы рендерите компонент <Modal />, родительский компонент может перехватить его события независимо от того, реализован ли он с помощью порталов.

Полезная ли эта страница?

© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://17.reactjs.org/docs/portals.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API