Portale

Portale bieten eine erstklassige Möglichkeit, Kinder in einen DOM-Knoten zu rendern, der außerhalb der DOM-Hierarchie der Eltern-Komponente existiert.

ReactDOM.createPortal(child, container)

Das erste Argument (child) ist ein beliebiges renderbares React-Kind, wie beispielsweise ein Element, eine Zeichenkette oder ein Fragment. Das zweite Argument (container) ist ein DOM-Element.

Verwendung

Wenn du ein Element aus der Render-Methode einer Komponente zurückgibst, wird es normalerweise als Kind des nächsten Eltern-Knotens in das DOM eingebunden:

render() {
  // React bindet ein neues div-Element ein und rendert die Kinder in diesem.
  return (
    <div>      {this.props.children}
    </div>  );
}

Manchmal ist es jedoch nützlich, ein Kind an einer anderen Position im DOM einzufügen:

render() {
  // React erstellt *keine* neue div. Es rendert die Kinder zu `domNode`.
  // `domNode` ist jeder valide DOM-Knoten, unabhängig von seiner Position im DOM.
  return ReactDOM.createPortal(
    this.props.children,
    domNode  );
}

Ein typischer Use-Case für Portale ist, wenn Eltern-Komponenten Styles mit den Attributen overflow: hidden oder z-index besitzen und du jedoch möchtest, dass das Kind visuell aus seinem Container “ausbricht”. Zum Beispiel bei Dialogen, Hovercards und Tooltips.

Hinweis:

Wenn du mit Portalen arbeitest, denk daran, dass das Verwalten des Tastaturfokus sehr wichtig wird.

Versichere dich bei Modal-Dialogen, dass jeder mit diesen interagieren kann, indem er die WAI-ARIA Modal Authoring Practices befolgt.

Probier es auf CodePen aus

Event-Bubbling durch Portale

Auch wenn sich ein Portal überall im DOM-Baum befinden kann, verhält es sich auf jede andere Weise wie ein normales React-Kind. Funktionen wie context funktionieren unabhängig davon, ob es sich bei dem Kind um ein Portal handelt, genau gleich, da das Portal unabhängig von der Position im DOM-Baum weiterhin im React-Baum existiert.

Dies schließt das Event-Bubbling mit ein. Ein Event, das innerhalb eines Portals ausgelöst wird, wird sich auf Vorfahren im enthaltenden React-Baum ausbreiten, auch wenn diese Elemente keine Vorfahren im DOM-Baum sein sollten. Unter der Annahme der folgenden HTML-Struktur:

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

Eine Eltern-Komponente in #app-root wäre in der Lage, ein unerreichtes, aufsteigendes Event aus dem Geschwister-Knoten #modal-root zu erreichen.

// Diese beiden Container sind Geschwister im 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() {
    // Das Portal-Element wird in den DOM-Baum eingefügt, nachdem
    // die Modal-Kinder eingebunden wurden, d.h. die Kinder werden
    // in einem freistehenden DOM-Node eingebunden. Wenn eine Kind-
    // Komponente beim Einbinden voraussetzt direkt an den DOM-Baum
    // angehängt zu werden, z.B. um einen DOM-Knoten zu messen oder wenn
    // sie 'autoFocus' in einem Nachfahren nutzt, füge dem Modal den  
    // Status hinzu und rendere die Kinder-Komponenten nur, wenn das 
    // Modal in den DOM-Baum eingefügt ist. 
    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() {    // Dies wird ausgelöst, wenn auf den Button im Kind geklickt wird,    // wodurch der Status der Eltern aktualisiert wird, auch wenn der    // Button im DOM kein direkter Nachfahre ist.    this.setState(state => ({      clicks: state.clicks + 1    }));  }
  render() {
    return (
      <div onClick={this.handleClick}>        <p>Anzahl der Klicks: {this.state.clicks}</p>
        <p>
          Öffne die Browser DevTools, 
          um zu sehen, dass der Button 
          kein Kind des div mit dem 
          onClick-Handler ist.
        </p>
        <Modal>          <Child />        </Modal>      </div>
    );
  }
}

function Child() {
  // Das Klick-Event auf diesem Button wird zu dem Elternelement aufsteigen,   // da kein 'onClick'-Attribut definiert ist.  return (
    <div className="modal">
      <button>Klick</button>    </div>
  );
}

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

Probier es auf CodePen aus

Das Auffangen eines Events, das von einem Portal in einer Eltern-Komponente aufsteigt, erlaubt die Entwicklung von flexibleren Abstraktionen, die nicht von Natur aus von Portalen abhängig sind. Wenn du zum Beispiel eine <Modal />-Komponente renderst, kann die Eltern-Komponente seine Events unabhängig davon, ob sie über Portale implementiert sind, erfassen.

Is this page useful?Bearbeite diese Seite