Tutorial

Erstellen von Wrapper-Komponenten in React mit Props

DevelopmentJavaScriptReact

Der Autor hat Creative Commons dazu ausgewählt, im Rahmen des Programms Write for DOnations eine Spende zu erhalten.

Einführung

In diesem Tutorial erstellen Sie Wrapper-Komponenten mit Props unter Verwendung der React JavaScript-Bibliothek. Wrapper-Komponenten sind Komponenten, die unbekannte Komponenten umgeben und eine Standardstruktur zum Anzeigen der untergeordneten Komponenten bereitstellen. Dieses Muster ist nützlich zur Erstellung von Elementen der Benutzeroberfläche (UI), die in einem Design wiederholt genutzt werden (zum Beispiel modale Elemente, Vorlagenseiten und Informationskacheln).

Um Wrapper-Komponenten zu erstellen, lernen Sie zunächst, wie man die Rest- und Spread-Operatoren verwendet, um ungenutzte Props zu sammeln und auf verschachtelte Komponenten zu übertragen. Dann erstellen Sie eine Komponente, die die native Komponente children nutzt, um verschachtelte Komponenten in JSX zu umschließen, als wären sie HTML-Elemente. Abschließend übergeben Sie Komponenten als Props zur Erstellung flexibler Wrapper, die an verschiedenen Stellen in einer Komponente benutzerdefinierte JSX einbetten können.

In diesem Tutorial erstellen Sie Komponenten, um eine Liste von Tierdaten in Form von Karten anzuzeigen. Sie werden lernen, wie man Daten aufteilt und Komponenten umgestaltet, während Sie flexible Wrapping-Komponenten einrichten. Am Ende des Tutorials werden Sie über eine funktionierende Anwendung verfügen, die erweiterte Prop-Verfahren nutzen wird, um wiederverwendbare Komponenten zu erstellen, die sich bei wachsender oder wandelnder Anwendung skalieren und anpassen lassen.

Hinweis: Im ersten Schritt wird ein leeres Projekt eingerichtet, auf dem Sie die Tutorial-Übung aufbauen. Wenn Sie bereits ein Arbeitsprojekt haben und direkt mit Requisiten arbeiten möchten, beginnen Sie mit Schritt 2.

Voraussetzungen

Schritt 1 - Erstellen eines leeren Projekts

In diesem Schritt erstellen Sie unter Verwendung der Create React App eine Grundlage für Ihr Projekt. Anschließend löschen Sie das Beispielprojekt und die zugehörigen Dateien, die beim Urladen des Projekts installiert werden. Schließlich erstellen Sie eine einfache Dateistruktur, um Ihre Komponenten zu organisieren. Auf diese Weise erhalten Sie eine solide Basis, auf der Sie im nächsten Schritt die Wrapper-Anwendung dieses Tutorials aufbauen können.

Beginnen Sie mit der Erstellung eines neuen Projekts. Führen Sie auf Ihrer Befehlszeile das folgende Skript aus, um ein neues Projekt mit create-react-app zu installieren:

  • npx create-react-app wrapper-tutorial

Nachdem das Projekt erstellt ist, wechseln Sie in das Verzeichnis:

  • cd wrapper-tutorial

Starten Sie das Projekt in einer neuen Terminal-Registerkarte oder einem -fenster mit dem Create React App start-Skript. Der Browser wird bei Änderungen automatisch aktualisiert. Lassen Sie das Skript während des Arbeitens also laufen:

  • npm start

Sie erhalten einen funktionierenden lokalen Server. Falls das Projekt nicht in einem Browserfenster geöffnet wurde, können Sie es über http://localhost:3000/ öffnen. Wenn Sie dies von einem Remote-Server aus ausführen, ist die Adresse http://your_domain:3000.

Ihr Browser wird mit einer einfachen React-Anwendung geladen, die Teil der Create React App ist:

React-Vorlagenprojekt

Sie werden ein vollständig neues Set von benutzerdefinierten Komponenten erstellen, sodass Sie zunächst etwas Standardcode löschen müssen, um ein leeres Projekt zu erhalten.

Zuerst öffnen Sie src/App.js in einem Texteditor. Das ist die Root-Komponente, die in der Seite injiziert ist. Alle Komponenten beginnen von hier. Weitere Informationen zu App.js finden Sie unter Wie man ein React-Projekt mit Create React App einrichtet.

Öffnen Sie src/App.js mit dem folgenden Befehl:

  • nano src/App.js

Sie sehen eine Datei wie diese:

wrapper-tutorial/src/App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

Löschen Sie die Zeile Logo aus '/logo.svg'; importieren. Ersetzen Sie dann alles in der return-Anweisung, um eine Reihe leerer Tags zurückzugeben: <></>. Dadurch erhalten Sie eine gültige Seite, die nichts zurückgibt. Der endgültige Code sieht so aus:

wrapper-tutorial/src/App.js

import React from 'react';
import './App.css';

function App() {
  return <></>;
}

export default App;

Speichern und schließen Sie den Texteditor.

Abschließend löschen Sie das Logo. Sie werden es nicht in Ihrer Anwendung verwenden und Sie sollten unbenutzte Dateien entfernen, während Sie arbeiten. Das wird Sie auf lange Sicht vor Unordnung bewahren.

Geben Sie im Terminalfenster den folgenden Befehl ein:

  • rm src/logo.svg

Wenn Sie in Ihren Browser schauen, sehen Sie einen leeren Bildschirm.

leerer Bildschirm in Chrome

Nachdem Sie das Beispielprojekt React App erstellen gelöscht haben, erstellen Sie eine einfache Dateistruktur. Dies hilft Ihnen, Ihre Komponenten isoliert und unabhängig zu halten.

Erstellen Sie im Verzeichnis src ein Verzeichnis mit dem Namen Komponenten. Es wird alle Ihre benutzerdefinierten Komponenten enthalten.

  • mkdir src/components

Jede Komponente verfügt über ein eigenes Verzeichnis zum Speichern der Komponentendatei zusammen mit den Stilen, gegebenenfalls Bildern und Tests.

Erstellen Sie ein Verzeichnis für App:

  • mkdir src/components/App

Verschieben Sie alle App-Dateien in dieses Verzeichnis. Verwenden Sie die wildcard, *, um alle mit App. beginnenden Dateien auszuwählen, unabhängig von der Dateierweiterung. Verwenden Sie dann den Befehl mv, um sie in das neue Verzeichnis zu platzieren.

  • mv src/App.* src/components/App

Aktualisieren Sie als Nächstes den relativen Importpfad in index.js. Dabei handelt es sich um die root-Komponente, die den gesamten Prozess bootet.

  • nano src/index.js

Die Import-Anweisung muss auf die Datei App.js im App-Verzeichnis verweisen. Nehmen Sie daher die folgende hervorgehobene Änderung vor:

wrapper-tutorial/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Speichern und schließen Sie die Datei.

Nachdem das Projekt eingerichtet wurde, können Sie Ihre erste Komponente erstellen.

Schritt 2 — Erfassen von unbenutzten Props mit ...props

In diesem Schritt erstellen Sie eine Komponente, um einen Datensatz über eine Gruppe von Tieren anzuzeigen. Ihre Komponente wird eine zweite verschachtelte Komponente enthalten, um einige Daten visuell darzustellen. Um die übergeordnete und verschachelte Komponente zu verbinden, nutzen Sie die Rest- und Spread-Operatoren, um ungenutzte Props vom übergeordneten Element an das untergeordnete Element zu übergeben, ohne dass das übergeordnete Element die Namen oder Arten der Props kennen muss.

Am Ende dieses Schritts verfügen Sie über eine übergeordnete Komponente, die Props für verschachtelte Komponenten bereitstellen kann, ohne wissen zu müssen, um welche Props es sich handelt. Dadurch bleibt die übergeordnete Komponente flexibel, sodass Sie die untergeordnete Komponente aktualisieren können, ohne das übergeordnete Element ändern zu müssen.

Erstellen einer AnimalCard-Komponente

Erstellen Sie zunächst einen Datensatz für Ihre Tiere. Öffnen Sie zuerst eine Datei mit den Daten im Verzeichnis components/App:

  • nano src/components/App/data.js

Fügen Sie die folgenden Daten hinzu:

src/components/App/data.js

export default [
  {
    name: 'Lion',
    scientificName: 'Panthero leo',
    size: 140,
    diet: ['meat']
  },
  {
    name: 'Gorilla',
    scientificName: 'Gorilla beringei',
    size: 205,
    diet: ['plants', 'insects']
  },
  {
    name: 'Zebra',
    scientificName: 'Equus quagga',
    size: 322,
    diet: ['plants'],
  }
]

Diese Liste mit Tieren ist ein Array von Objekten, das den Namen, den wissenschaftlichen Namen, das Gewicht und die Ernährung des jeweiligen Tiers beinhaltet.

Speichern und schließen Sie die Datei.

Erstellen Sie als Nächstes ein Verzeichnis für die AnimalCard-Komponente:

  • mkdir src/components/AnimalCard

Öffnen Sie eine neue Datei im Verzeichnis:

  • nano src/components/AnimalCard/AnimalCard.js

Fügen Sie nun eine Komponente hinzu, die den Namen, die Ernährung und die Größe als Prop nutzen und anzeigen wird.

wrapper-tutorial/src/components/AnimalCard/AnimalCard.js
import React from 'react';
import PropTypes from 'prop-types';

export default function AnimalCard({ diet, name, size }) {
  return(
    <div>
      <h3>{name}</h3>
      <div>{size}kg</div>
      <div>{diet.join(', ')}.</div>
    </div>
  )
}

AnimalCard.propTypes = {
  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
  name: PropTypes.string.isRequired,
  size: PropTypes.number.isRequired,
}

Hier destrukturieren Sie die Props in der Parameterliste für die Funktion AnimalCard und zeigen die Daten dann in einem div an. Die diet-Daten werden mit der Methode join() als einzelne Zeichenfolge aufgelistet. Jedes Datenstück enthält einen entsprechenden PropType, um sicherzustellen, dass der Datentyp korrekt ist.

Speichern und schließen Sie die Datei.

Nachdem Sie über Ihre Komponente und Ihre Daten verfügen, müssen Sie sie nun miteinander kombinieren. Importieren Sie dazu die Komponente und die Daten in die root-Komponente Ihres Projekts: App.js.

Öffnen Sie zunächst die Komponente:

  • nano src/components/App/App.js

Von dort aus können Sie eine Schleife über die Daten durchlaufen und eine neue AnimalCard mit den entsprechenden Props zurückgeben. Fügen Sie die hervorgehobenen Zeilen zu App.js hinzu:

wrapper-tutorial/src/components/App/App.js
import React from 'react';
import './App.css';

import animals from './data';
import AnimalCard from '../AnimalCard/AnimalCard';

function App() {
  return (
    <div className="wrapper">
      {animals.map(animal =>
        <AnimalCard
          diet={animal.diet}
          key={animal.name}
          name={animal.name}
          size={animal.size}
        />
      )}
    </div>
  );
}

export default App;

Speichern und schließen Sie die Datei.

Wenn Sie an komplexeren Projekten arbeiten, werden Ihre Daten aus diverseren Quellen stammen (z. B. APIs, lokalem Speicher oder statischen Dateien). Der Prozess zur Verwendung der jeweiligen Daten ähnelt sich jedoch: Weisen Sie die Daten einer Variable zu und durchlaufen Sie eine Schleife über die Daten. In diesem Fall stammen sie aus einer statischen Datei, sodass Sie sie direkt in eine Variable importieren.

In diesem Code nutzen Sie die .map()-Methode, um über animals zu iterieren und die Props anzuzeigen. Beachten Sie, dass Sie nicht alle Daten verwenden müssen. Sie übergeben beispielsweise die Eigenschaft scientificName nicht explizit. Außerdem fügen Sie einen separaten Schlüssel-Prop hinzu, den React zum Verfolgen der abgebildeten Daten nutzen wird. Schließlich umschließen Sie den Code durch ein div mit dem className wrapper, den Sie zum Hinzufügen von Formatierungselementen nutzen werden.

Öffnen Sie zum Hinzufügen der Formatierungselemente App.css:

  • nano src/components/App/App.css

Entfernen Sie die Formatierungsbausteine und fügen Sie flex properties einer Klasse namens wrapper hinzu:

prop-tutorial/src/components/App/App.js
.wrapper {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    padding: 20px;
}

Dadurch wird ein Flexbox-Layout genutzt, um die Daten zu organisieren und auszurichten. padding schafft ein wenig Platz im Browserfenster, während justify-content den Platz zwischen Elementen vergrößert.

Speichern und schließen Sie die Datei. Wenn Sie dies tun, wird der Browser aktualisiert und Sie sehen einige Daten im Abstand.

Browser mit vergrößertem Abstand zwischen Daten

Erstellen einer Detailkomponente

Sie verfügen nun über eine einfache Komponente, die die Daten anzeigt. Nehmen wir jedoch an, dass Sie die diet-Daten etwas aufpeppen möchten, indem Sie den Text in ein Emoji verwandeln. Sie können dazu die Daten in Ihrer Komponente konvertieren.

React ist so konzipiert, dass es besonders flexibel ist. Wenn Sie also überlegen, wie Sie Daten konvertieren sollen, haben Sie verschiedene Optionen:

  • Sie können eine Funktion innerhalb der Komponente erstellen, die den Text in ein Emoji umwandelt.
  • Sie können eine Funktion erstellen und in einer Datei außerhalb der Komponente speichern, um die Logik in verschiedenen Komponenten wiederverwenden zu können.
  • Sie können eine separate Komponente erstellen, die den Text in ein Emoji umwandelt.

Jeder Ansatz ist in Ordnung, wenn er auf den richtigen Anwendungsfall angewendet wird; Sie werden beim Einrichten einer Anwendung wahrscheinlich zwischen den verschiedenen Ansätzen wechseln. Um vorzeitige Abstraktion und Komplexität zu vermeiden, sollten Sie mit der ersten Option starten. Wenn Sie Logik wiederverwenden möchten, können Sie die Funktion separat aus der Komponente extrahieren. Die dritte Option ist die beste, wenn Sie ein wiederverwendbares Stück haben möchten, das die Logik und das Markup enthält oder das Sie in der Anwendung isoliert verwenden möchten.

In diesem Fall erstellen wir eine neue Komponente, da wir später mehr Daten hinzufügen wollen und Markup mit der Konvertierungslogik kombinieren.

Die neue Komponente erhält den Namen AnimalDetails. Erstellen Sie dazu ein neues Verzeichnis:

  • mkdir src/components/AnimalDetails

Öffnen Sie als Nächstes AnimalDetails.js in Ihrem Texteditor:

  • nano src/components/AnimalDetails/AnimalDetails.js

Erstellen Sie in der Datei eine kleine Komponente, die die diet (Ernährung) als Emoji anzeigt:

wrapper-tutorial/src/components/AnimalDetails/AnimalDetails.js
import React from 'react';
import PropTypes from 'prop-types';
import './AnimalDetails.css';

function convertFood(food) {
  switch(food) {
    case 'insects':
      return '🐜';
    case 'meat':
      return '🍖';
    case 'plants':
    default:
      return '🌱';
  }
}

export default function AnimalDetails({ diet }) {
  return(
    <div className="details">
      <h4>Details:</h4>
      <div>
        Diet: {diet.map(food => convertFood(food)).join(' ')}
      </div>
    </div>
  )
}

AnimalDetails.propTypes = {
  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
}

Das Objekt AnimalDetails.propTypes richtet die Funktion so ein, dass ein Prop von diet verwendet wird. Dabei handelt es sich um ein Array von Zeichenfolgen. Dann durchläuft der Code in der Komponente eine Schleife über diet und wandelt die Zeichenfolge mit der Anweisung switch in ein Emoji um.

Speichern und schließen Sie die Datei.

Außerdem importieren Sie einige CSS. Fügen wir das also jetzt hinzu.

Öffnen Sie AnimalDetails.css:

  • nano src/components/AnimalDetails/AnimalDetails.css

Fügen Sie einige CSS hinzu, um dem Element einen Rahmen und Rand zu verleihen und die Details vom Rest der Komponente zu trennen:

wrapper-tutorial/src/components/AnimalDetails/AnimalDetails.css
.details {
    border-top: gray solid 1px;
    margin: 20px 0;
}

Wir nutzen .details, um die Regel mit Elementen, die den className details tragen, abzustimmen.

Speichern und schließen Sie die Datei.

Nachdem Sie über eine neue benutzerdefinierte Komponente verfügen, können Sie sie nun Ihrer AnimalCard-Komponente hinzufügen. Öffnen Sie AnimalCard.js:

  • nano src/components/AnimalCard/AnimalCard.js

Ersetzen Sie die Anweisung diet.join durch die neue AnimalDetails-Komponente und übergeben Sie diet als Prop, indem Sie die hervorgehobenen Zeilen hinzufügen:

wrapper-tutorial/src/components/AnimalCard/AnimalCard.js
import React from 'react';
import PropTypes from 'prop-types';
import AnimalDetails from '../AnimalDetails/AnimalDetails';

export default function AnimalCard({ diet, name, size }) {
  return(
    <div>
      <h3>{name}</h3>
      <div>{size}kg</div>
      <AnimalDetails
        diet={diet}
      />
    </div>
  )
}

AnimalCard.propTypes = {
  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
  name: PropTypes.string.isRequired,
  size: PropTypes.number.isRequired,
}

Speichern Sie die Datei; die neuen Details werden im Browser angezeigt.

Browser mit Details

Übergeben von Details über eine Komponente mit ...props

Die Komponenten funktionieren gut zusammen, in AnimalCard gibt es jedoch eine kleine Unzulänglichkeit. Sie extrahieren diet explizit aus dem Argument props, nutzen die Daten aber nicht. Stattdessen übergeben Sie sie an die Komponente. Daran ist nichts grundsätzlich falsch: So ist es oft besser, bei zu viel Kommunikation auf der sicheren Seite zu bleiben. Das erschwert jedoch die Pflege Ihres Codes. Wenn Sie neue Daten an AnimalDetails übergeben möchten, müssen Sie drei Stellen aktualisieren: App, wo Sie die Props übergeben, AnimalDetails, was das Prop konsumiert, und AnimalCard, was als Vermittler dient.

Eine bessere Methode besteht darin, alle ungenutzten Props in AnimalCard zu sammeln und diese dann direkt an AnimalDetails zu übergeben. Dadurch erhalten Sie die Möglichkeit, Änderungen an AnimalDetails vorzunehmen, ohne AnimalCard zu ändern. In Wahrheit muss AnimalCard nichts über die Props oder PropTypes wissen, die in AnimalDetails einfließen.

Dazu verwenden Sie den object rest-Operator. Dieser Operator sammelt alle Elemente, die während der Destrukturierung nicht extrahiert werden, und speichert sie in einem neuen Objekt.

Hier ist ein einfaches Beispiel:

const dog = {
    name: 'dog',
    diet: ['meat']
}

const { name, ...props  } = dog;

In diesem Fall werden die Variable name 'dog' und die Variable props { diet: ['meat']} lauten.

Bisher haben Sie alle Props so übergeben, als wären sie HTML-Attribute; Sie können zum Senden von Props jedoch auch Objekte verwenden. Um ein Objekt als Prop zu verwenden, müssen Sie den Spread-Operator – ...props – mit geschweiften Klammern umgeben. Dadurch wird jedes Schlüssel-Wert-Paar in ein Prop geändert.

Öffnen Sie AnimalCard.js:

  • nano src/components/AnimalCard/AnimalCard.js

Entfernen Sie diet aus dem destrukturierten Objekt und sammeln Sie stattdessen den Rest der Props in einer Variable namens props. Übergeben Sie diese Props dann direkt an AnimalDetails:

wrapper-tutorial/src/components/AnimalCard/AnimalCard.js
import React from 'react';
import PropTypes from 'prop-types';
import AnimalDetails from '../AnimalDetails/AnimalDetails';

export default function AnimalCard({ name, size, ...props }) {
  return(
    <div>
      <h3>{name}</h3>
      <div>{size}kg</div>
      <AnimalDetails
        {...props}
      />
    </div>
  )
}

AnimalCard.propTypes = {
  name: PropTypes.string.isRequired,
  size: PropTypes.number.isRequired,
}

Beachten Sie, dass Sie den diet-PropType entfernen können, da Sie das Prop in dieser Komponente nicht verwenden.

In diesem Fall übergeben Sie nur ein Prop an AnimalDetails. In Fällen, in denen Sie über mehrere Props verfügen, ist die Reihenfolge von Bedeutung. Ein späteres Prop wird frühere Props überschreiben. Wenn Sie über ein Prop verfügen, das Priorität haben soll, müssen Sie also sicherstellen, dass es an letzter Stelle steht. Dies kann zu Verwirrung führen, wenn Ihr props-Objekt eine Eigenschaft hat, die auch ein benannter Wert ist.

Speichern und schließen Sie die Datei. Der Browser wird aktualisiert und alles wird unverändert aussehen:

Browser mit Details

Um zu sehen, wie das Objekt ...props die Flexibilität erhöht, übergeben wir den scientificName an AnimalDetails über die Komponente AnimalCard.

Öffnen Sie zunächst App.js:

  • nano src/components/App/App.js

Übergeben Sie dann den scientificName als Prop:

wrapper-tutorial/src/components/App/App.js
import React from 'react';
import './App.css';

import animals from './data';
import AnimalCard from '../AnimalCard/AnimalCard';

function App() {
  return (
    <div className="wrapper">
      {animals.map(animal =>
        <AnimalCard
          diet={animal.diet}
          key={animal.name}
          name={animal.name}
          size={animal.size}
          scientificName={animal.scientificName}
        />
      )}
    </div>
  );
}

export default App;

Speichern und schließen Sie die Datei.

Überspringen Sie AnimalCard; hier müssen Sie keine Änderungen vornehmen. Öffnen Sie dann AnimalDetails, um das neue Prop zu verwenden:

  • nano src/components/AnimalDetails/AnimalDetails.js

Das neue Prop ist eine Zeichenfolge, die Sie der Liste details zusammen mit einer Zeile hinzufügen, die den PropType deklariert:

wrapper-tutorial/src/components/AnimalDetails/AnimalDetails.js
import React from 'react';
...
export default function AnimalDetails({ diet, scientificName }) {
  return(
    <div className="details">
      <h4>Details:</h4>
      <div>
        Scientific Name: {scientificName}.
      </div>
      <div>
        Diet: {diet.map(food => convertFood(food)).join(' ')}
      </div>
    </div>
  )
}

AnimalDetails.propTypes = {
  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
  scientificName: PropTypes.string.isRequired,
}

Speichern und schließen Sie die Datei. Danach wird der Browser aktualisiert und Sie sehen die neuen Details ohne jegliche Änderungen an der Komponente AnimalCard:

Browser mit dem wissenschaftlichen Namen

In diesem Schritt haben Sie gelernt, wie Sie flexible übergeordnete Props erstellen, die unbekannte Props nutzen und mit dem Spread-Operator in verschachtelte Komponenten übergeben können. Dies ist ein häufiges Muster, das Ihnen die Flexibilität verleiht, die Sie zur Erstellung von Komponenten mit fokussierten Aufgaben benötigen. Im nächsten Schritt erstellen Sie Komponenten, die mit dem nativen Prop children unbekannte Komponenten als Prop verwenden können.

Schritt 3 — Erstellen von Wrapper-Komponenten mit children

In diesem Schritt erstellen Sie eine Wrapper-Komponente, die eine unbekannte Gruppe von Komponenten als Prop verwenden kann. Dadurch erhalten Sie die Möglichkeit, Komponenten wie Standard-HTML zu verschachteln. Außerdem erhalten Sie ein Muster zum Erstellen von wiederverwendbaren Wrappern, sodass Sie eine Vielzahl von Komponenten erstellen können, die ein einheitliches Design bei einen flexiblen Inneren aufweisen.

React bietet Ihnen ein natives Prop namens children, das alle untergeordneten Komponenten sammelt. Dadurch wird die Erstellung von Wrapper-Komponenten intuitiv und lesbar.

Erstellen Sie zunächst eine neue Komponente namens Card. Das wird eine Wrapper-Komponente sein, um ein neues Standardformat für alle neuen Kartenkomponenten zu erstellen.

Erstellen Sie ein neues Verzeichnis:

  • mkdir src/components/Card

Öffnen Sie dann die Komponente Card in Ihrem Texteditor:

  • nano src/components/Card/Card.js

Erstellen Sie eine Komponente, die children und title als Props verwendet und in einem div umschließt, indem Sie den folgenden Code hinzufügen:

wrapper-tutorial/src/components/Card/Card.js
import React from 'react';
import PropTypes from 'prop-types';
import './Card.css';

export default function Card({ children, title }) {
  return(
    <div className="card">
      <div className="card-details">
        <h2>{title}</h2>
      </div>
      {children}
    </div>
  )
}

Card.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.element),
    PropTypes.element.isRequired
  ]),
  title: PropTypes.string.isRequired,
}

Die PropTypes für die children (untergeordneten Elemente) sind neu. Das Prop children kann entweder ein JSX-Element oder ein Array von JSX-Elementen sein. Der title (Titel) ist eine Zeichenfolge.

Speichern und schließen Sie die Datei.

Fügen Sie als Nächstes einige Formatierungselemente hinzu. Öffnen Sie Card.css:

  • nano src/components/Card/Card.css

Ihre Karte wird einen Rahmen und eine Linie unter den Details aufweisen.

wrapper-tutorial/src/components/Card/Card.css
.card {
    border: black solid 1px;
    margin: 10px;
    padding: 10px;
    width: 200px;
}

.card-details {
    border-bottom: gray solid 1px;
    margin-bottom: 20px;
}

Speichern und schließen Sie die Datei. Nachdem Sie über Ihre Komponente verfügen, müssen Sie sie verwenden. Sie könnten jede AnimalCard mit der Komponente Card in App.js umschließen, da der Name AnimalCard jedoch impliziert, dass es sich bereits um eine Card handelt, wäre es besser, die Komponente Card innerhalb von AnimalCard zu verwenden.

Öffnen Sie AnimalCard:

  • nano src/components/AnimalCard/AnimalCard.js

Im Gegensatz zu anderen Props übergeben Sie children jedoch nicht explizit. Stattdessen schließen Sie die JSX, als wären sie untergeordnete HTML-Elemente. Anders ausgedrückt: Sie verschachteln sie einfach innerhalb des Elements (wie folgt):

wrapper-tutorial/src/components/AnimalCard/AnimalCard.js
import React from 'react';
import PropTypes from 'prop-types';
import Card from '../Card/Card';
import AnimalDetails from '../AnimalDetails/AnimalDetails';

export default function AnimalCard({ name, size, ...props }) {
  return(
    <Card title="Animal">
      <h3>{name}</h3>
      <div>{size}kg</div>
      <AnimalDetails
        {...props}
      />
    </Card>
  )
}

AnimalCard.propTypes = {
  name: PropTypes.string.isRequired,
  size: PropTypes.number.isRequired,
}

Anders als bei einer React-Komponente müssen Sie kein einziges root-Element als untergeordnetes Element verwenden. Deshalb hat der PropType für Card angegeben, dass es sich um ein Array von Elementen oder ein einzelnes Element handeln kann. Zusätzlich zur Übergabe der children (untergeordneten Elemente) als verschachtelte Komponenten geben Sie der Karte den Namen Animal.

Speichern und schließen Sie die Datei. Danach wird der Browser aktualisiert und Sie sehen die aktualisierte Kartenkomponente.

Browser mit Karten

Sie verfügen nun über eine wiederverwendbare Card-Komponente, die eine beliebige Anzahl von verschachtelten untergeordneten Elementen aufnehmen kann. Der Hauptvorteil besteht darin, dass Sie die Card (Karte) mit beliebigen Komponenten wiederverwenden können. Würden Sie eine Plant-Karte erstellen wollen, könnten Sie dazu die Pflanzendaten mit der Card-Komponente umschließen. Es muss nicht einmal eine Beziehung vorhanden sein: Sollten Sie die Card-Komponente in einer völlig anderen Anwendung wiederverwenden wollen, die zum Beispiel Musik oder Kontodaten auflisten, ist das ebenfalls möglich. Der Komponente Card ist es egal, woraus die untergeordneten Elemente bestehen; Sie verwenden das Wrapper-Element einfach wieder (in diesem Fall den formatierten Rahmen und Titel).

Der Nachteil von children besteht darin, dass es lediglich eine Instanz des untergeordneten Prop geben kann. Hin und wieder benötigen Sie aber wahrscheinlich eine Komponente, die benutzerdefinierte JSX an mehreren Stellen aufweist. Zum Glück können Sie dazu JSX- und React-Komponenten als Props übergeben, worauf wir im nächsten Schritt eingehen werden.

Schritt 4 — Übergeben von Komponenten als Props

In diesem Schritt verändern Sie Ihre Card-Komponente, damit sie andere Komponenten als Props nutzt. Dadurch erhält Ihre Komponente maximale Flexibilität, um unbekannte Komponenten oder JSX auf der Seite an verschiedenen Stellen anzuzeigen. Im Gegensatz zu children (untergeordneten Elementen), die Sie nur einmal verwenden können, können Sie so viele Komponenten wie Props nutzen. So lässt sich Ihre Wrapper-Komponente an eine Vielzahl von Bedürfnissen anpassen, während die standardmäßige Optik und Struktur gewahrt bleiben.

Am Ende dieses Schritts werden Sie über eine Komponente verfügen, die untergeordnete Komponenten umschließen und auch andere Komponenten in der Karte anzeigen kann. Dieses Muster bietet Ihnen Flexibilität, wenn Sie Komponenten erstellen möchten, die Daten benötigen, die komplexer als einfache Zeichenfolgen und Ganzzahlen sind.

Wir wollen die Komponente Card so ändern, dass sie ein willkürliches React-Element namens details nutzt.

Öffnen Sie zunächst die Card-Komponente:

  • nano src/components/Card/Card.js

Fügen Sie als Nächstes ein neues Prop namens details hinzu und platzieren Sie es unter dem Element <h2>:

wrapper-tutorial/src/components/Card/Card.js
import React from 'react';
import PropTypes from 'prop-types';
import './Card.css';

export default function Card({ children, details, title }) {
  return(
    <div className="card">
      <div className="card-details">
        <h2>{title}</h2>
        {details}
      </div>
      {children}
    </div>
  )
}

Card.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.element),
    PropTypes.element.isRequired
  ]),
  details: PropTypes.element,
  title: PropTypes.string.isRequired,
}

Card.defaultProps = {
  details: null,
}

Dieses Prop wird den gleichen Typ wie children aufweisen, sollte jedoch optional sein. Um es optional zu machen, fügen Sie einen Standardwert von null hinzu. In diesem Fall wird die Komponente, wenn ein Benutzer keine Details übergibt, dennoch gültig sein und nichts Zusätzliches anzeigen.

Speichern und schließen Sie die Datei. Die Seite wird aktualisiert und Sie sehen das gleiche Bild wie zuvor:

Browser mit Karten

Fügen Sie der AnimalCard nun einige Details hinzu. Öffnen Sie zunächst AnimalCard.

  • nano src/components/AnimalCard/AnimalCard.js

Da die Card-Komponente children bereits verwendet, müssen Sie die neue JSX-Komponente als Prop übergeben. Da es sich jeweils um Säugetiere handelt, fügen Sie dies der Karte hinzu, umschließen es jedoch mit <em>-Tags, um Kursivschrift anzuwenden.

wrapper-tutorial/src/components/AnimalCard/AnimalCard.js
import React from 'react';
...

export default function AnimalCard({ name, size, ...props }) {
  return(
    <Card title="Animal" details={<em>Mammal</em>}>
      <h3>{name}</h3>
      <div>{size}kg</div>
      <AnimalDetails
        {...props}
      />
    </Card>
  )
}
...

Speichern Sie die Datei. Daraufhin wird der Browser aktualisiert und Sie sehen die Aktualisierung, einschließlich der Phrase Säugetier.

Browser mit Karte und Details

Dieses Prop ist bereits leistungsstark, da es JSX beliebiger Größe verwenden kann. In diesem Beispiel haben Sie nur ein einziges Element hinzugefügt; Sie könnten jedoch so viele JSX übergeben, wie Sie wollen. Es muss auch nicht JSX sein. Wenn Sie beispielsweise über kompliziertes Markup verfügen, würden Sie es nicht direkt im Prop übergeben wollen. Das wäre schwierig zu lesen. Stattdessen könnten Sie eine separate Komponente erstellen und die Komponente dann als Prop übergeben.

Um dies in der Praxis zu sehen, übergeben Sie AnimalDetails an das Prop details:

wrapper-tutorial/src/components/AnimalCard/AnimalCard.js
import React from 'react';
...

export default function AnimalCard({ name, size, ...props }) {
  return(
    <Card
      title="Animal"
      details={
        <AnimalDetails
          {...props}
        />
      }
    >
      <h3>{name}</h3>
      <div>{size}kg</div>
    </Card>
  )
}
...

AnimalDetails ist komplizierter und weist eine Reihe von Markup-Zeilen auf. Würden Sie dies direkt zu details hinzufügen, würde das Prop wesentlich größer und damit schwerer zu lesen.

Speichern und schließen Sie die Datei. Daraufhin wird der Browser aktualisiert und die Details erscheinen oben auf der Karte.

Karte mit Details oben

Sie verfügen nun über eine Card-Komponente, die benutzerdefinierte JSX verwenden und an verschiedenen Stellen platzieren kann. Sie sind nicht auf einzelne Props beschränkt; vielmehr können Sie Elemente an so viele Props übergeben, wie Sie möchten. So können Sie flexible Wrapping-Komponenten erstellen, die anderen Entwicklern die Möglichkeit bieten, eine Komponente anzupassen, während ihre Gesamtformatierung und -funktionalität beibehalten werden.

Das Übergeben einer Komponente als Prop ist nicht perfekt. Ein Prop ist etwas schwieriger zu lesen und nicht so klar wie das Übergeben von children (untergeordneten Elementen). Es ist jedoch genauso flexibel und kann in einer Komponente so oft verwendet werden, wie Sie möchten. Sie sollten children (untergeordnete Elemente) zuerst verwenden, aber nicht zögern, auf Props zurückzugreifen, wenn das nicht ausreicht.

In diesem Schritt haben Sie gelernt, wie sich JSX- und React-Komponenten als Props an eine andere Komponente übergeben lassen. Dadurch erhält Ihre Komponente die Flexibilität, für viele Situationen einsetzbar zu sein, in denen eine Wrapper-Komponente mehrere Props zum Verwalten von JSX oder Komponenten benötigt.

Zusammenfassung

Sie haben eine Vielzahl von Wrapping-Komponenten erstellt, die Daten flexibel anzeigen können, während eine vorhersehbare Optik und Struktur gewahrt bleiben. Sie haben Komponenten erstellt, die unbekannte Props aufnehmen und an verschachtelte Komponenten übergeben können. Außerdem haben Sie das native Prop children verwendet, um Wrapper-Komponenten zu erstellen, die eine beliebige Anzahl von verschachtelten Elementen handhaben können. Abschließend haben Sie eine Komponente erstellt, die JSX- oder React-Komponenten als Prop nutzen können, damit Ihre Wrapper-Komponente mehrere Instanzen mit verschiedenen Anpassungen verarbeiten kann.

Wrapper-Komponenten bieten Ihnen die Möglichkeit, Anpassungen an unbekannte Umstände vorzunehmen sowie die Wiederverwendbarkeit und Konsistenz von Code zu maximieren. Dieses Muster ist nützlich zur Erstellung von grundlegenden UI-Elementen, die Sie in einer Anwendung häufiger wiederverwenden. Dazu gehören Schaltflächen, Warnmeldungen, modale Elemente, Diashows und mehr. Sie werden sehen, dass Sie immer wieder dahin zurückkehren werden.

Wenn Sie weitere React-Tutorials konsultieren möchten, sehen Sie sich unsere React-Themenseite an oder kehren Sie zurück zur Seite der Reihe Codieren in React.js.

Creative Commons License