We want to hear from you!Take our 2021 Community Survey!

Portals

Portals fornece uma forma elegante de renderizar um elemento filho dentro de um nó DOM que existe fora da hierarquia do componente pai.

ReactDOM.createPortal(child, container)

O primeiro argumento (child) é qualquer elemento filho React renderizável, como um elemento, string ou fragmento. O segundo argumento (container) é um elemento DOM.

Utilização

Normalmente, quando retornamos um elemento pelo método render de um componente ele é montado dentro do DOM como um filho do nó pai mais próximo:

render() {
  // React monta uma nova div e renderiza o filho dentro dela
  return (
    <div>      {this.props.children}
    </div>  );
}

Entretanto, em algumas situações é útil inserir um elemento filho em um local diferente no DOM:

render() {
  // React *não* cria uma nova div. Ele renderiza os filhos dentro do `domNode`.
  // `domNode` é qualquer nó DOM válido, independente da sua localização no DOM.
  return ReactDOM.createPortal(
    this.props.children,
    domNode  );
}

Um caso típico do uso de portals é quando um componente pai tem o estilo overflow: hidden ou z-index, mas você precisa que o filho visualmente “saia” desse contêiner. Por exemplo, caixas de diálogo, hovercards e tooltips.

Nota:

Quando estiver trabalhando com portals, lembre-se que tratar o evento focus se torna muito importante.

No caso dos modals, assegure-se que todos possam interagir com eles seguindo as práticas descritas em WAI-ARIA Modal Authoring Practices.

Experimente no CodePen

Propagação de Eventos Através do Portals

Apesar de um portal poder estar em qualquer lugar na árvore DOM, seu comportamento é como o de qualquer outro elemento React filho. Funcionalidades como contexto funcionam da mesma forma independente se o filho é um portal, pois o portal ainda existe na árvore React independentemente da posição que esteja na árvore DOM.

Isso inclui a propagação de eventos. Um evento disparado dentro de um portal será propagado para os elementos antecessores da árvore React, mesmo que estes não sejam antecessores na árvore DOM. Considere a seguinte estrutura HTML:

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

Um componente Pai em #app-root será capaz de capturar a propagação de um evento não tratado vindo do nó irmão #modal-root.

// Estes dois contêineres são irmãos no 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() {
    // O elemento portal é inserido na árvore DOM depois que
    // os componentes filhos de `Modal` são montados, o que significa que os filhos
    // serão montados em um nó DOM separado. Se um componente
    // filho precisa ser colocado na árvore DOM
    // imediatamente quando é montado, por exemplo para medir um
    // nó DOM ou usar 'autoFocus' em um descendente, adicione
    // state ao Modal e renderize o filho apenas quando o Modal
    // estiver inserido na árvore DOM.
    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() {    // Isso é disparado quando o botão no filho é clicado,    // atualizando o state do componente Pai, mesmo que o filho    // não seja um descendente direto no DOM.    this.setState(state => ({      clicks: state.clicks + 1    }));  }
  render() {
    return (
      <div onClick={this.handleClick}>        <p>Número de cliques: {this.state.clicks}</p>
        <p>
          Abra o DevTools do navegador
          para observar que o botão
          não é um filho da div
          com o onClick.
        </p>
        <Modal>          <Child />        </Modal>      </div>
    );
  }
}

function Child() {
  // O evento de clique nesse botão irá propagar para o ascendente,  // porque o atributo 'onClick' não está definido  return (
    <div className="modal">
      <button>Clicar</button>    </div>
  );
}

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

Experimente no CodePen

Capturar um evento propagado a partir de um portal em um componente pai permite o desenvolvimento de abstrações mais flexíveis que não dependem diretamente de portals. Por exemplo, se você renderizar um componente <Modal />, o componente pai pode capturar seus eventos independentemente se são implementados usando portals.

Esta página é útil?Edite esta página