Gutenberg lernen: React 101

Avatar of Andy Bell
Von Andy Bell am

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

Obwohl Gutenberg mit React erstellt wurde, ist der Code, den wir zum Erstellen benutzerdefinierter Blöcke schreiben, das nicht. Er ähnelt zwar definitiv einer React-Komponente, daher denke ich, dass es nützlich ist, ein wenig damit zu spielen, um sich mit dieser Art von Ansatz vertraut zu machen. Bisher gab es in dieser Reihe viel zu *lesen*, also krempeln wir die Ärmel hoch und *machen* etwas Cooles.

Artikelserie

  1. Reihen-Einführung
  2. Was ist Gutenberg eigentlich?
  3. Eine Einführung mit create-guten-block
  4. Moderne JavaScript-Syntax
  5. React 101 *(Dieser Beitrag)*
  6. Ein benutzerdefiniertes Webpack einrichten
  7. Ein benutzerdefinierter "Card"-Block

Lassen Sie uns eine "Über mich"-Komponente erstellen

Wir werden eine einzelne React-Komponente erstellen, die die Hintergrundfarbe einer Seite und den Einführungstext basierend auf von Ihnen eingegebenen Daten aus ein paar Feldern aktualisiert. *"Ich dachte, das sollte cool sein",* höre ich Sie alle murmeln. Ich gebe zu, ich habe es vielleicht übertrieben, aber wir werden einige Kernkonzepte von **zustandsgesteuertem JavaScript** lernen, die uns nützlich sein werden, wenn wir uns mit unserem Gutenberg-Block befassen.

Zur Referenz, das ist, was wir am Ende bekommen werden

Erste Schritte

Als Erstes werden wir CodePen starten. CodePen kann kostenlos genutzt werden, also gehen Sie dorthin und erstellen Sie einen neuen Pen.

Als Nächstes werden wir einige JavaScript-Abhängigkeiten hinzufügen. Es gibt drei Editor-Bildschirme – finden Sie den `JS`-Bildschirm und klicken Sie auf das Einstellungen-Zahnrad. Dies öffnet ein Fenster "Pen-Einstellungen", in dem Sie den Abschnitt **Externe Skripte/Pens hinzufügen** finden. Ganz unten gibt es ein Dropdown-Menü **Schnell hinzufügen**. Öffnen Sie es.

A screenshot of the CodePen interface with the JavaScript settings open. The settings are in a split pane where the settings are on the left in a white box and advanced settings are on the right in a dark gray box.

Wählen Sie im Menü **React**. Sobald dies ausgewählt ist, öffnen Sie das Menü und wählen Sie **ReactDOM**. Sie werden sehen, dass dadurch einige Textfelder vorausgefüllt wurden.

Zuletzt müssen wir unseren ES6-Code aktivieren. Wählen Sie also im Menü **JavaScript-Präprozessor** **Babel**.

Klicken Sie nun auf die große Schaltfläche **Speichern & schließen**.

Was wir dort getan haben, ist, die Haupt-React-JS-Bibliothek und die ReactDOM-Bibliothek geladen zu haben. Diese ermöglichen es uns, einzutauchen und unseren Code zu schreiben, was unser nächster Schritt ist.

Richten Sie unser CSS ein

Lassen Sie es cool aussehen. Zuerst richten wir unseren CSS-Editor ein. Als Erstes richten wir ihn so ein, dass er Sass für uns kompiliert. Genau wie beim JS-Editor klicken Sie auf das Einstellungen-Zahnrad, das wieder das Modal **Pen-Einstellungen** öffnet – diesmal mit den CSS-Einstellungen.

Oben befindet sich ein Menü **CSS-Präprozessor**. Wählen Sie dort **SCSS** aus.

Wenn das erledigt ist, gehen Sie zu **Externe Stylesheets/Pens hinzufügen** und fügen Sie die folgenden drei Links in separate Textfelder ein

https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.css
https://fonts.googleapis.com/css?family=Work+Sans:300
https://rawgit.com/hankchizljaw/boilerform/master/dist/css/boilerform.min.css

Diese drei geben uns nacheinander ein Reset, eine schicke Schriftart und einige hilfreiche Formularstile.

Nachdem nun alles eingerichtet ist, klicken Sie erneut auf die Schaltfläche "Speichern & schließen".

Fügen Sie etwas Stil hinzu

Wir sind also bereit, dieser Schritt sollte einfach sein. Fügen Sie das folgende Sass in den CSS-Editor ein

:root {
  --text-color: #f3f3f3;
}

* {
  box-sizing: border-box;
}

html {
  height: 100%;
  font-size: 16px;
}

body {
  height: 100%;
  position: relative;
  font-size: 1rem;
  line-height: 1.4;
  font-family: "Work Sans", sans-serif;
  font-weight: 300;
  background: #f3f3f3;
  color: #232323;
}

.about {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  color: var(--text-color);
  transition: all 2000ms ease-in-out;
  
  &__inner {
    display: flex;
    flex-direction: column;
    height: 100%;
    margin: 0 auto;
    padding: 1.2rem;
  }
  
  &__content {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    flex: 1 1 auto;
    font-size: 3rem;
    line-height: 1.2;
    
    > * {
      max-width: 30ch;
    }
  }
  
  &__form {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 2rem 0;
    width: 100%;
    max-width: 60rem;
    margin: 0 auto;

    @media(min-width: 32rem) {
      flex-direction: row;
      justify-content: space-between;
      padding: 2rem;
    }
    
    > * {
      width: 15rem;
    }
    
    > * + * {
      margin: 1rem 0 0 0;
      
      @media(min-width: 32rem) {
        margin: 0;
      }
    }
    
    label {
      display: block;
    }
  }
}

// Boilerform overrides 
.c-select-field {
  &,
  &__menu {
    width: 100%;
  }
}

.c-input-field {
  width: 100%;
}

.c-label {
  color: var(--text-color);
}

Das ist ein großer Brocken CSS, und es wird aussehen, als sei nichts wirklich passiert, aber das ist gut so – wir müssen uns für den Rest dieses Abschnitts keine Sorgen mehr um CSS machen.

Tauchen wir in React ein

Als Erstes geben wir React etwas zum Anheften. Fügen Sie dies in den HTML-Editor Ihres Pens ein

<div id="root"></div>

Das war's für HTML – Sie können Ihren JS-Editor vergrößern, damit wir uns voll und ganz darauf konzentrieren können.

Beginnen wir unseren Komponenten-Code, indem wir eine neue Instanz einer React-Komponente erstellen, indem wir den folgenden JavaScript-Code schreiben

class AboutMe extends React.Component {
}

Was dieser Code tut, ist die Erstellung einer neuen `AboutMe`-Komponente und die Erweiterung der `Component`-Klasse von React, was uns eine Menge Code und Tools kostenlos zur Verfügung stellt.

Nun, wir haben eine Klasse, und jetzt müssen wir sie konstruieren! Fügen Sie den folgenden Code innerhalb der Klammern hinzu

constructor(props) {
  super(props);
  
  let self = this;
};

Hier passiert einiges, daher werde ich jeden Punkt erklären

`constructor` ist die Methode, die aufgerufen wird, wenn Sie `new AboutMe()` schreiben, oder wenn Sie `` in Ihrem JSX schreiben. Dies konstruiert das Objekt. Der `props`-Parameter ist etwas, das Sie in React oft sehen werden. Dies ist die Sammlung von Eigenschaften, die in die Komponente übergeben werden. Zum Beispiel: Wenn Sie `` schreiben würden, könnten Sie in Ihrem `constructor` mit `props.name` darauf zugreifen.

`super` ist, wie wir der Klasse, die wir erweitert haben, sagen, dass sie mit ihrem eigenen `constructor` konstruieren soll. Sie werden sehen, dass wir auch die `props` weitergeben, falls übergeordnete Komponenten darauf zugreifen müssen.

Schließlich ist `let self = this` eine Möglichkeit, den Geltungsbereich von `this` zu steuern. Denken Sie daran, da wir `let` verwenden, ist `self` nur innerhalb der `constructor`-Funktion verfügbar.

Kurzer Hinweis für die Leser, die mit JavaScript nicht so sicher sind: Ich habe festgestellt, dass ein tieferer Einblick in den **Geltungsbereich** von JavaScript zu vielen "Aha"-Momenten in meinem Lernprozess geführt hat. Ich empfehle dringend die Buchreihe *You Don’t Know JS* von Kyle Simpson (kostenlos auf GitHub erhältlich!). Bemerkenswerte Bände: `this` und Objektprototypen und Geltungsbereich & Closures. Gutes Zeug, versprochen.

Nachdem wir nun den Konstruktor behandelt haben, fügen wir ihm weiteren Code hinzu. Fügen Sie nach der Zeile `let self = this;` den folgenden Code ein

self.availableColors = [
  {
    "name": "Red",
    "value": "#ca3814"
  },
  {
    "name": "Blue",
    "value": "#0086cc"
  },
  {
    "name": "Green",
    "value": "#3aa22b"
  }
];

Was wir dort haben, ist ein Array von Objekten, die unsere Optionen für die Auswahl Ihrer Lieblingsfarbe definieren. Fügen Sie Ihre eigene hinzu, wenn sie noch nicht vorhanden ist!

Ihre Klassendefinition und Ihr Konstruktor sollten jetzt so aussehen

class AboutMe extends React.Component {
  
  constructor(props) {
    super(props);
    
    let self = this;
    
    // Set a list of available colors that render in the select menu
    self.availableColors = [
      {
        "name": "Red",
        "value": "#ca3814"
      },
      {
        "name": "Blue",
        "value": "#0086cc"
      },
      {
        "name": "Green",
        "value": "#3aa22b"
      }
    ];
  };
}

Bis hierhin ziemlich einfach, oder? Lassen Sie uns weitermachen und einige Anfangswerte für unseren reaktiven Zustand festlegen. Fügen Sie Folgendes nach dem Schließen von `self.availableColors` hinzu

// Set our initial reactive state values
self.state = {
  name: 'Foo',
  color: self.availableColors[0].value
};

Diese anfängliche Zustandsfestlegung ermöglicht es unserer Komponente, beim Laden sowohl einen Namen als auch eine Farbe zu rendern, was verhindert, dass sie defekt aussieht.

Als Nächstes fügen wir unsere `render`-Funktion hinzu. Dies ist eine reine Funktion, die nichts weiter tut, als die Komponente basierend auf dem anfänglichen Zustand oder Zustandsänderungen während des Lebenszyklus der Komponente zu rendern. Sie haben es vielleicht schon erraten, aber hier befindet sich der Hauptteil unseres JSX.

Warten Sie! Was ist eine *reine Funktion*? Willkommen in der funktionalen Programmierung, einem Top-Thema in der React-Welt. *Reine* Funktionen sind Funktionen, bei denen für die Eingabe X die Ausgabe immer Y ist. In einer "unreinen" Funktion kann die Eingabe X je nach anderen Teilen des Programms zu unterschiedlichen Ausgaben führen. Hier ist ein CodePen, der reine und unreine Funktionen vergleicht. Schauen Sie sich auch diesen Artikel an, für weitere Details.

Da wir jetzt recht viel Markup in dieser einzelnen Komponente haben, werden wir das Ganze in unsere Funktion kopieren. Fügen Sie Folgendes unter Ihrem `constructor` hinzu

render() {
  let self = this;
  
  return (
    <main className="about" style={ { background: self.state.color } }>
      <section className="about__inner">
        <article className="about__content">
          { self.state.name ? <p>Hello there. My name is { self.state.name }, and my favourite color is { self.getActiveColorName() }</p> : null }
        </article>
        <form className="[ about__form ] [ boilerform ]">
          <div>
            <label className="c-label" htmlFor="name_field">Your name</label>
            <input className="c-input-field" type="text" id="name_field" value={ self.state.name } onChange={ self.updateName.bind(self) } />
          </div>
          <div>
            <label className="c-label" htmlFor="color_field">Your favourite color</label>
            <div className="c-select-field">
              <select className="c-select-field__menu" value={ self.state.color } onChange={ self.updateColor.bind(self) } id="color_field">
                { self.availableColors.map((color, index) => {
                  return (
                    <option key={ index } value={ color.value }>{ color.name }</option>
                  );
                })}
              </select>
              <span className="c-select-field__decor" aria-hidden="true" role="presentation">▾</span>
            </div>
          </div>
        </form>
      </section>
    </main>
  );
};

Sie denken vielleicht: "Heilige Kuh, hier passiert ja eine Menge." Lassen Sie es uns sezieren, also machen Sie sich keine Sorgen ums Kopieren von Code – ich werde Sie informieren, wann wir das wieder tun. Konzentrieren wir uns vorerst nur auf einige Schlüsselbereiche.

In JSX müssen Sie ein einzelnes Element zurückgeben, das Kindelemente enthalten kann. Da unser gesamter Code in einem `<main>`-Tag gekapselt ist, sind wir dort gut aufgestellt. Bei diesem `<main>`-Tag sehen Sie, dass wir einen Ausdruck in einem Attribut haben, wie wir ihn in Teil 2 behandelt haben. Dieser Ausdruck setzt die Hintergrundfarbe als die aktuell in unserem Zustand gesetzte aktive Farbe. Diese wird sich wie von Zauberhand aktualisieren, wenn ein Benutzer seine Farbwahl ändert, ohne dass wir dafür eine weitere Codezeile in dieser Render-Funktion schreiben müssen. Ziemlich cool, oder?

Innerhalb des `<article class="about__content">`-Elements werden Sie Folgendes bemerken

{ self.state.name ? <p>Hello there. My name is { self.state.name }, and my favourite color is { self.getActiveColorName() }</p> : null }

Dieser ternäre Operator prüft, ob ein Name gesetzt ist und rendert entweder einen Satz, der den Namen enthält, oder `null`. `null` in JSX zurückzugeben ist, wie Sie dem Client sagen, nichts zu rendern. Ebenfalls mit diesem Snippet verwandt: Wir konnten diesen ternären Operator innerhalb unseres JSX ausführen, da wir durch das Öffnen von Klammern einen Ausdruck erstellt haben. Es ist eine sehr nützliche Möglichkeit, kleine, einfache Anzeigelogiken in Ihre `render`-Funktion einzustreuen.

Als Nächstes betrachten wir eine Ereignisbindung

<input className="c-input-field" type="text" id="name_field" value={ self.state.name } onChange={ self.updateName.bind(self) } />

Wenn Sie kein Ereignis an Ihr Eingabefeld binden, ist es schreibgeschützt. Aber keine Panik, falls Sie es vergessen. React warnt Sie hilfreich in Ihrer Konsole.

Denken Sie daran, `self` ist gleich `this`, also fügen wir die Funktion `updateName` an das `onChange`-Ereignis des Eingabefeldes an, aber wir binden auch `self`, sodass, wenn wir uns innerhalb der Funktion `updateName` befinden, `this` gleich `AboutMe` ist, was unsere Komponente ist.

Das Letzte, was wir uns in der `render`-Funktion ansehen werden, sind Schleifen. Hier ist das Snippet, das das Farbauswahlmenü rendert

<select className="c-select-field__menu" value={ self.state.color } onChange={ self.updateColor.bind(self) } id="color_field">
  { self.availableColors.map((color, index) => {
    return (
      <option key={ index } value={ color.value }>{ color.name }</option>
    );
  }) }
</select>

Die Werte- und Änderungskonfiguration ist die gleiche wie beim obigen ``-Element, daher ignorieren wir sie und tauchen direkt in die Schleife ein. Wir haben einen Ausdruck geöffnet, in dem wir eine ziemlich Standard-Array-Map-Funktion ausführen, aber wichtig ist, dass sie in jeder Iteration JSX zurückgibt, was es jeder Option ermöglicht, mit dem Rest des JSX gerendert zu werden.

Alles zusammenfügen

Nachdem wir nun die Kernaspekte der Komponente am Laufen haben, müssen wir sie zusammenfügen. Sie werden feststellen, dass Ihr CodePen im Moment nichts tut. Das liegt an zwei Dingen

  • Wir haben die Komponente noch nicht an das DOM angehängt
  • Wir haben noch keine Methoden geschrieben, um sie interaktiv zu machen

Beginnen wir mit ersterem und fügen unsere Änderungsereignisbehandler hinzu. Fügen Sie dies unterhalb Ihres `constructor`-Funktion hinzu

updateName(evt) {
  let self = this;
  
  self.setState({
    name: evt.target.value
  })
};

updateColor(evt) {
  let self = this;
  
  self.setState({
    color: evt.target.value
  })
};

Diese beiden Funktionen behandeln die `onChange`-Ereignisse des `` und setzen ihre Werte im Zustand mit Reacts `setState`-Funktion. Da die Werte nun im Zustand sind, wird alles, was daran abonniert ist, automatisch aktualisiert. Das bedeutet, dass sich die ternäre Anweisung, die Ihren Namen und die Hintergrundfarbe rendert, in Echtzeit ändert, während Sie tippen/auswählen. Genial. Oder?

Nun wäre es empfehlenswert, Ihren Code DRYer zu gestalten, indem Sie diese beiden zu einem einzigen Änderungsereignisbehandler kombinieren, der den relevanten Zustand aktualisiert. Für diese Reihe halten wir die Dinge jedoch einfach und verständlicher. 😀

Als Nächstes fügen wir die letzte Methode zu unserer Komponente hinzu. Fügen Sie dies unter Ihren kürzlich hinzugefügten Update-Methoden hinzu

// Return active color name from available colors, based on state value
getActiveColorName() {
  let self = this;
  
  return self.availableColors.filter(color => color.value === self.state.color)[0].name;
};

Diese Funktion verwendet eine meiner bevorzugten JavaScript-Array-Methoden: `filter`. Mit ES6 können wir Array-Elemente basierend auf ihrem Objektwert in einer Zeile auswählen, was mächtig ist. Mit dieser Macht können wir den menschlich lesbaren Namen des aktuell aktiven `availableColors`-Elements auswählen und zurückgeben.

JavaScript-Array-Methoden sind sehr cool und werden im React-Ökosystem häufig eingesetzt. Sarah Drasner hat einen erstaunlichen "Array Explorer" erstellt – schauen Sie ihn sich hier an!

Die Komponente an das DOM anhängen

Als Letztes hängen wir unsere Komponente mithilfe von ReactDOM an das DOM an. Was wir tun, ist zu sagen: "Hey Browser, hol mir das `

`-Element und rendere diese React-Komponente darin." ReactDOM macht die ganze Magie, die das möglich macht.

ReactDOM ist ein sehr intelligentes Paket, das Änderungen in Ihren dynamischen React-Komponenten aufnimmt, berechnet, was im DOM geändert werden muss, und diese Änderungen auf die effizienteste Weise anwendet. Mit der `renderToString()`-Methode von ReactDOM können Sie auch Ihre React-Komponenten in eine statische Zeichenfolge rendern, die dann mit Ihrem serverseitigen Code in Ihre Seite eingefügt werden kann. Das Geniale daran ist, dass Referenzen hinzugefügt werden, sodass, wenn Ihr Frontend einige serverseitig gerenderte React-Komponenten erkennt, es herausfindet, welche Komponenten benötigt werden und den gesamten Block statischer Markup automatisch dynamisch macht. Ziemlich genial, oder?

Aber zurück zu unserem Pen. Fügen Sie dies ganz unten in Ihren JS-Editor ein

// Attach our component to the <div id="root"> element
ReactDOM.render(<AboutMe />, document.getElementById('root'));

Nun werden Sie feststellen, dass Ihr Vorschaufenster plötzlich lebendig geworden ist! Herzlichen Glückwunsch – Sie haben gerade eine React-Komponente geschrieben 🎉

Sehen Sie den Pen About Me React Component von Andy Bell (@hankchizlja) auf CodePen.

Zusammenfassung

In diesem Teil haben Sie durch das Schreiben einer React-Komponente etwas über reaktives, komponentenbasiertes JavaScript gelernt. Dies ist für Ihr Lernen relevant, da benutzerdefinierte Gutenberg-Blöcke eine sehr ähnliche Einrichtung wie eine React-Komponente haben. Da Sie nun ein besseres Verständnis dafür haben, wie eine React-Komponente funktioniert, sollten Sie auch verstehen können, wie ein benutzerdefinierter Gutenberg-Block funktioniert.

Es hat mich etwas Zeit gekostet, mich damit abzufinden, dass React im Kontext von Gutenberg nur für die Erstellung von Blöcken innerhalb des Admin-Bereichs relevant ist. In Gutenberg fungiert React als Mittel zur Vorbereitung des Markup, das in der `post_content`-Spalte in der Datenbank gespeichert werden soll. Die Verwendung von React auf dem Frontend einer WordPress-Website zum Erstellen von etwas wie diesem wäre von dem getrennt, was wir in dieser Reihe tun werden.

Als Nächstes in dieser Reihe werden wir unser WordPress-Theme bearbeiten, damit wir unseren benutzerdefinierten Gutenberg-Block erstellen können.


Artikelserie

  1. Reihen-Einführung
  2. Was ist Gutenberg eigentlich?
  3. Eine Einführung mit create-guten-block
  4. Moderne JavaScript-Syntax
  5. React 101 *(Dieser Beitrag)*
  6. Ein benutzerdefiniertes Webpack einrichten
  7. Ein benutzerdefinierter "Card"-Block