Verwendung von React Portalen zum Rendern von Kindern außerhalb der DOM-Hierarchie

Avatar of Kingsley Silas
Kingsley Silas am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Angenommen, wir müssen ein Kindelement in einer React-Anwendung rendern. Einfach, oder? Dieses Kind wird an das nächstgelegene DOM-Element angehängt und dementsprechend darin gerendert.

render() {
  return (
    <div>
      // Child to render inside of the div
    </div>
  );
}

Aber! Was, wenn wir dieses Kind *außerhalb* des Divs woanders rendern wollen? Das kann knifflig sein, da es die Konvention bricht, dass eine Komponente als neues Element gerendert werden und einer Eltern-Kind-Hierarchie folgen muss. Der Elternteil möchte dorthin gehen, wo sein Kind hingeht.

Hier kommen React Portals ins Spiel. Sie bieten eine Möglichkeit, Elemente außerhalb der DOM-Hierarchie zu rendern, damit Elemente etwas portabler sind. Es ist vielleicht keine perfekte Analogie, aber Portale sind so etwas wie die Rohre in Mario Bros., die dich aus dem normalen Spielfluss transportieren und in eine andere Region bringen.

Das Coole an Portalen? Auch wenn sie ihre eigenen Ereignisse auslösen, die unabhängig vom Elternelement des Kindes sind, hört der Elternteil immer noch auf diese Ereignisse, was nützlich sein kann, um Ereignisse in einer App weiterzugeben.

Wir werden in diesem Beitrag gemeinsam ein Portal erstellen und es dann zu einer wiederverwendbaren Komponente machen. Los geht's!

Das Beispiel, das wir erstellen

Hier ist ein relativ einfaches Beispiel für ein Portal in Aktion

Siehe den Pen React Portal von Kingsley Silas Chijioke (@kinsomicrote) auf CodePen.

Das Umschalten der Sichtbarkeit eines Elements ist nichts Neues. Wenn Sie sich jedoch den Code genau ansehen, werden Sie feststellen, dass das ausgegebene Element vom Button gesteuert wird, obwohl es kein direkter Nachkomme davon ist. Tatsächlich, wenn Sie den Quellcode mit der gerenderten Ausgabe in DevTools vergleichen, sehen Sie die Beziehung

Das Elternelement des ausgegebenen Elements hört also tatsächlich auf das Klicken des Buttons und erlaubt, dass das Kind eingefügt wird, obwohl es und der Button separate Geschwister im DOM sind. Lassen Sie uns die Schritte zur Erstellung dieses umzuschaltbaren Portal-Elements aufschlüsseln, um zu sehen, wie alles funktioniert.

Schritt 1: Das Portal-Element erstellen

Die erste Zeile einer React-Anwendung gibt an, dass ein App-Element im Dokument-Root mit ReactDOM gerendert wird. So:

ReactDOM.render(<App />, document.getElementById("root"));

Wir müssen das App-Element in einer HTML-Datei platzieren, um es auszuführen

<div id="App"></div>

Ähnlich wie bei Portalen. Als erstes erstellen wir zum Erstellen eines Portals ein neues Div-Element in der HTML-Datei.

<div id="portal"></div>

Dieses Div dient als unser Ziel. Wir verwenden `#portal` als ID, aber das muss nicht so sein. Jede Komponente, die innerhalb dieses Ziel-Divs gerendert wird, behält den Kontext von React. Wir müssen das Div als Wert einer Variablen speichern, damit wir die Portal-Komponente, die wir erstellen werden, nutzen können

const portalRoot = document.getElementById("portal");

Sieht dem Aufruf des App-Elements sehr ähnlich, richtig?

Schritt 2: Eine Portal-Komponente erstellen

Als Nächstes richten wir das Portal als Komponente ein

class Portal extends React.Component {
  constructor() {
    super();
    // 1: Create a new div that wraps the component
    this.el = document.createElement("div");
  }
  // 2: Append the element to the DOM when it mounts
  componentDidMount = () => {
    portalRoot.appendChild(this.el);
  };
  // 3: Remove the element when it unmounts
  componentWillUnmount = () => {
    portalRoot.removeChild(this.el);
  };
  render() {
    // 4: Render the element's children in a Portal
    const { children } = this.props;
    return ReactDOM.createPortal(children, this.el);
  }
}

Lassen Sie uns einen Schritt zurücktreten und betrachten, was hier passiert.

Wir erstellen im Konstruktor ein neues Div-Element und setzen es als Wert für `this.el`. Wenn die Portal-Komponente gemountet wird, wird `this.el` als Kind zu diesem Div in der HTML-Datei hinzugefügt, wo wir es hinzugefügt haben. Das ist in unserem Fall die Zeile `<div id="portal"></div>`.

Der DOM-Baum wird so aussehen.

<div> // Portal, which is also portalRoot
  <div> // this.el
  </div>
</div>

Wenn Sie neu in React sind und mit dem Konzept des Mountens und Unmountens eines Elements verwirrt sind, hat Jake Trent eine gute Erklärung. TL;DR: Mounten ist der Moment, in dem das Element in den DOM eingefügt wird.

Wenn die Komponente unmounted wird, wollen wir das Kind entfernen, um Speicherlecks zu vermeiden. Wir werden diese Portal-Komponente in eine andere Komponente importieren, wo sie verwendet wird, nämlich in das Div, das in unserem Beispiel den Header und den Button enthält. Dabei werden wir die Kind-Elemente der Portal-Komponente mitnehmen. Deshalb haben wir `this.props.children`.

Schritt 3: Das Portal verwenden

Um die Kind-Elemente der Portal-Komponente zu rendern, verwenden wir `ReactDOM.createPortal()`. Dies ist eine spezielle ReactDOM-Methode, die die Kinder und das erstellte Element akzeptiert. Um zu sehen, wie das Portal funktioniert, verwenden wir es in unserer App-Komponente.

Bevor wir das tun, behandeln wir die Grundlagen, wie die App funktionieren soll. Wenn die App geladen wird, wollen wir einen Text und einen Button anzeigen – wir können dann den Button umschalten, um die Portal-Komponente ein- oder auszublenden.

class App extends React.Component {
  // The initial toggle state is false so the Portal element is out of view
  state = {
    on: false
  };

  toggle = () => {
    // Create a new "on" state to mount the Portal component via the button
    this.setState({
      on: !this.state.on
    });
  };
  // Now, let's render the components
  render() {
    const { on } = this.state;
    return (
      // The div where that uses the Portal component child
      <div>
        <header>
          <h1>Welcome to React</h1>
        </header>
        <React.Fragment>
          // The button that toggles the Portal component state
          // The Portal parent is listening for the event
          <button onClick={this.toggle}>Toggle Portal</button>
          // Mount or unmount the Portal on button click
          <Portal>
            {
              on ?
                <h1>This is a portal!</h1>
              : null
            }
          </Portal>
        </React.Fragment>
      </div>
    );
  }
}

Da wir das Portal ein- und ausschalten wollen, müssen wir den Komponentenstatus verwenden, um das Umschalten zu verwalten. Dies ist im Grunde eine Methode, um den Status `on` bei einem Klickereignis entweder auf `true` oder `false` zu setzen. Das Portal wird gerendert, wenn `on` true ist; andernfalls rendern wir nichts.

So sieht der DOM aus, wenn der `on`-Status auf `true` gesetzt ist.

Wenn `on` `false` ist, wird die Portal-Komponente nicht im Root gerendert, daher sieht der DOM so aus.

Weitere Anwendungsfälle

Modals sind ein perfekter Kandidat für Portale. Tatsächlich verwenden die React-Dokumente sie als Hauptbeispiel für die Funktionsweise von Portalen

Siehe den Pen Example: Portals von Dan Abramov (@gaearon) auf CodePen.

Es ist das gleiche Konzept, bei dem eine Portal-Komponente erstellt wird und ein Zustand verwendet wird, um die Kind-Elemente an die Modal-Komponente anzuhängen.

Wir können sogar Daten von einer externen Quelle in ein Modal einfügen. In diesem Beispiel listet die App-Komponente Benutzer auf, die von einer API mithilfe von axios abgerufen wurden.

Siehe den Pen React Portal 3 von Kingsley Silas Chijioke (@kinsomicrote) auf CodePen.

Wie wäre es mit Tooltips? David Gilberston hat eine schöne Demo

Siehe den Pen React Portal Tooptip von David Gilbertson (@davidgilbertson) auf CodePen.

J Scott Smith zeigt, wie Portale zum Entkommen von Positionierungen verwendet werden können

Er hat ein weiteres schickes Beispiel, das das Einfügen von Elementen *und* die Verwaltung von Zuständen demonstriert

Zusammenfassung

Das war's! Hoffentlich gibt Ihnen dies ein solides Grundverständnis von Portalen, was sie sind, was sie tun und wie man sie in einer React-Anwendung verwendet. Das Konzept mag trivial erscheinen, aber die Fähigkeit, Elemente außerhalb der DOM-Hierarchie zu verschieben, ist eine praktische Möglichkeit, Komponenten etwas erweiterbarer und wiederverwendbarer zu machen ... all das weist auf die Kernvorteile der Verwendung von React an erster Stelle hin.

Weitere Informationen