Inhaltsverzeichnis


Nichts verpassen?

reactjs ÔÇó babel | Oliver Zeigermann ÔÇó | 8 Minuten

Einleitung

React mit seinem JSX-Format erfordert einen ├ťbersetzer in ein vom Browser ausf├╝hrbares JavaScript. Dieser ├ťbersetzer nennt sich Babel. Da Babel der ├ťbersetzer f├╝r React ist, k├Ânnen wir neben JSX auch alle tollen neuen Eigenschaften von ECMAScript 2015 (auch bekannt als ES6) nutzen. Es h├Ârt aber nicht dort auf, Babel unterst├╝tzt bereits jetzt eine ganze Reihe von ES7-Features, die gerade f├╝r React sehr praktisch sind und sogar zum Teil von den React-Entwicklern kommen.

Diese wollen wir uns hier einmal kurz angucken. Damit du den Artikel ganz verstehen kannst, solltest du zumindest schon einmal ein Hello-World mit React gebaut haben, es reicht aber Kenntnis ├╝ber die alte ES5-Variante.

Falls ihr noch nichts mit React gemacht habt, guckt euch am besten die Einf├╝hrung in React auf diesem Blog an.

React-Komponente mit ES6

Moderner React-Code, der mit ES6 geschrieben wird, sieht bereits sehr lesbar aus und nutzt so viele Standard-JavaScript-Muster wie m├Âglich. So ist eine typische React-Komponente einfach nur eine Klasse, die von React.Component erbt. Properties f├╝r die Komponente werden in den Konstruktor ├╝bergeben und Callbacks vom JSX sind einfache Methoden:

import React from 'react';

import MessageDisplay from './MessageDisplay';

export default class HelloMessage extends React.Component {
  constructor(props) {
    super(props);
    this.state = { greeting: this.props.greeting };
    this.updateModel = this.updateModel.bind(this);
    this.reset = this.reset.bind(this);
  }

  reset() {
    this.setState({ greeting: "" });
    React.findDOMNode(this.refs.in).focus();
  }

  updateModel(event) {
    this.setState({ greeting: event.target.value });
  }

  render() {
    return (
      <div>
        <input ref="in"
          onChange={this.updateModel}
          value={this.state.greeting} />
        <MessageDisplay
          greeting={this.state.greeting} />
        <button
          onClick={this.reset}>Clear
                </button>
      </div>);
  }
}

Die Callback-Methoden sind aber leider noch nicht an this gebunden, wenn ihr ES6-Klassen schreibt. Stattdessen binden wir diese Callbacks einmalig im Konstruktor. Es w├Ąre auch m├Âglich, dies direkt bei der Angabe des Callbacks im JSX zu tun, dann w├╝rden wir aber bei jedem Re-Rendering erneut ein Binding durchf├╝hren.

React selber wird durch ein ES6-Import eingebunden, und Unterkomponente sind jeweils nur ein weiteres ES6-Modul. In unserem Fall w├Ąre das die Komponente MessageDisplay und die sieht so aus:

import React from 'react';

export default class MessageDisplay extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return <p>{this.props.greeting}, World</p>;
  }
}

ES7 functionBind

Obwohl dies schon h├╝bsch aussieht, gibt es immer noch Punkte, die man verbessern k├Ânnte. Das erw├Ąhnte Binding zum Beispiel ist syntaktisch nicht gerade elegant. Es gibt als einen Vorschlag f├╝r ES7 nun aber eine einfachere Syntax f├╝r das Binding.

Der Vorschlag ist etwas umfangreicher, aber f├╝r uns springt eine einfache Ersetzung des Bindungs-Codes heraus. Unser Konstruktor w├╝rde damit so aussehen:

constructor(props) {
  // ...
  this.updateModel = ::this.updateModel;
  this.reset = ::this.reset;
}

Wir haben also so etwas wie this.updateModel.bind(this) durch das einfachere ::this.updateModel ersetzt.

ES7 classProperties

Wer Sprachen wie Java, C++ oder C# kennt, wird in ES6 vielleicht immer noch das direkte Deklarieren und Initialisieren von Properties in Klassen vermissen. Das geht mit einem weiteren ES7 Vorschlag und s├Ąhe bei unserem Beispiel nun so aus:

export default class HelloMessage extends React.Component {
  updateModel = ::this.updateModel;
  reset = ::this.reset;
  state = {greeting: this.props.greeting};

  // Kein Konstruktor mehr n├Âtig
  // ...
}

Die Zuweisung der gebundenen Methoden ist also vom Konstruktor in die Klassendefinition gerutscht. Das finde ich nun ein bisschen ├╝bersichtlicher und auch logischer. Dass der Code vorher im Konstruktor war, lag ja nur daran, dass es bislang (ES6) keine andere M├Âglichkeit gegeben hatte.

Aber auch der State kann direkt als Feld mit initialisiert werden und rutscht ebenfalls aus dem Konstruktor heraus. Da dieser danach nicht mehr notwendig ist und nur noch den Super-Konstruktor aufrufen w├╝rde, haben wir ihn in unserem Beispiel ganz gel├Âscht.

ES7 objectRestSpread

Unsere Komponente MessageDisplay bekommt von HelloMessage das Property greeting ├╝bergeben:

<MessageDisplay
       greeting={this.state.greeting} />

Wunderbar. Was nun aber, wenn wir eine ganze Reihe von Properties haben? Dann m├╝ssten wir f├╝r jedes Property auch ein neues Name-/Wert-Paar ├╝bergeben. Das kann f├╝r eine gro├če Menge von Properties sehr l├Ąstig und lang werden. Manche Entwickler verzichten daf├╝r sogar auf eine ├ťbergabe und nutzen stattdessen lieber den React Context, auf den alle Komponenten zugreifen k├Ânnen. Allerdings ist dieser ein etwas fragw├╝rdiges Konzept und bislang auch nirgendwo dokumentiert.

Stattdessen gibt es nun einen Vorschlag f├╝r einen Spread-Operator, der nicht nur f├╝r Arrays und andere Iterables, wie in ES6, sondern auch f├╝r ganze Objekte funktioniert.

So, kann ich dann ein komplettes Objekt an eine Unterkomponente ├╝bergeben, in unserem Fall ├╝bergebe ich einfach den kompletten Zustand:

 <MessageDisplay
     {...this.state}
     />

Das sieht f├╝r unseren Fall noch nicht so viel besser aus, aber f├╝r viele Properties ist das durchaus eine Option. In der React-Dokumentation wird ausdr├╝cklich auf diese Option verwiesen und sie wird auch empfohlen.

Unserer komplett auf ES7 umgebaute Komponente sieht nun so aus:

export default class HelloMessage extends React.Component {
  // es7.functionBind, es7.classProperties
  updateModel = ::this.updateModel;
  reset = ::this.reset;
  state = { greeting: this.props.greeting };

  reset() {
    this.setState({ greeting: "" });
    React.findDOMNode(this.refs.in).focus();
  }
  updateModel(event) {
    this.setState({ greeting: event.target.value });
  }
  render() {
    return (
      <div>
        <input ref="in"
          onChange={this.updateModel}
          value={this.state.greeting} />
        <MessageDisplay
          {...this.state} // es7.objectRestSpread
        />
        <button
          onClick={this.reset}>Clear</button>
      </div>);
  }
}

Fazit

Keines der hier gezeigten ES7-Features ist bisher im Draft-Stadium, d.h. ihr m├╝sst diese Features explizit bei Babel anschalten. Ich habe ein kleines Repo gebaut, in dem ich das f├╝r euch schon vorbereitet habe und das auch das obige Beispiel als lauff├Ąhigen Code enth├Ąlt.

Die Features k├Ânnen sich noch ├Ąndern oder ├╝berhaupt nicht in den Standard aufgenommen werden. F├╝r langlebige Projekte, solltet ihr euch also ├╝berlegen, ob ihr nicht doch lieber auf sie verzichten wollt. Die ES6-Features sind ja auch schon ganz nett. Diese k├Ânnt ihr ohne Einschr├Ąnkung verwenden, da ES6 schon final spezifiziert ist.

Nutzt ihr bereits die hier beschriebenen Features? Gibt es weitere ES6- oder ES7-Features, die ihr mit React nutzt? Hinterlasst doch bitte einen Kommentar oder schreibt mir auf Twitter unter @DJCordhose.