React

written by devangelist on Dezember 1, 2014 in Allgemein and Frameworks and Javascript and React with 3 comments

Gefühlt jeden Tag entsteht ein neues JavaScript Framework, aber es sind nur wenige, die das Interesse von genügend Entwicklern wecken, um auf dem hartumkämpften Markt eine Chance zu haben. Während gestern noch von Angular und Ember die Rede war, setzten viele heute auf React. Und es ist nicht nur, weil Facebook hinter dem JavaScript-Framework steht, sondern weil React ein paar Eigenschaften mitbringt, die bei den Konkurrenten vermisst wurden.

React wurde von Facebook ins Leben gerufen und wird nicht nur vom Erfinder eingesetzt, sondern auch andere Plattformen wie Instagram sind komplett in React entwickelt. Das Framework soll helfen, interaktive, zustandsorientierte und wiederverwendbare UI Komponenten zu erstellen.

React beats SEO

Das Isomorphe Framework kann nicht nur auf dem Client, sondern auf auf dem Server verwendet werden – was für viele ein großer Knackpunkt sein kann – denn auch wenn Suchmaschinen wie Google mittlerweile JavaScript verstehen und interpretieren können, leben alle bloß von der Theorie.

Wenn eine Webseite von Zugängen aus Google abhängig ist, wird hoffentlich niemand auf die Idee kommen, diese komplett auf dem Client zu laden. Vielleicht ändert sich diese seit Jahren kritisierte Theorie, aber solang das nicht der Fall ist, muss gewünscht oder nicht, die Webseite auf dem Server gerendert werden.

Virtual DOM

React versucht DOM Manipulationen so gering wie möglich halten. Mit dem Virtual DOM werden nur bestimmte Unterbäume von Knoten anhand von State-Änderungen gerendert. Das lässt sich am Besten anhand eines konkreten Beispiels erklären:

Angenommen es gibt ein Model. Und mit Model ist ein (nicht-so-top) Model á la Heidi Klum gemeint. Alle relevanten Eigenschaften dieses Models (Augen, Maße, …) werden auf den aktuellen State des Objekts gespiegelt. Dies ist prinzipiell genau das, was React mit dem DOM macht.

Möchte dieses Model nun wirklich ein Top-Model werden und dabei nicht auf bestimmte Eingriffe und einem strengen Fitnessprogramm verzichten, dann würde React wie folgt damit umgehen: Erst werden alle Änderungen, welche gerade stattfanden, identifiziert (Reconcilation). Anschließend wird das DOM mit den Änderungen aktualisiert.

React würde aber nicht ein komplett neues Model erstellen, sondern nur bestimmte Teile des Körpers ändern. Soll ein DOM Element neu gerendert werden, sich aber sein Elternelement nicht verändert hat, so wird dieses nicht angefasst.

React arbeitet aber immer mit einem fake-DOM, was auch das Rendern auf dem Server ermöglicht.

Getting started

Auf der getting started Seite von React kann ein Starter Kit geladen werden, oder noch auch direkt auf JSFiddle losgelegt werden.

Auf der eigenen Seite reicht es, react.js und JSXTransformer.js einzubinden. Die JSX-Komponenten werden in einem Script block mit type="text/jsx" erstellt.

<!DOCTYPE html>
<html>
  <head>
    <script src="build/react.js"></script>
    <script src="build/JSXTransformer.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/jsx">
      React.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
      );
    </script>
  </body>
</html>

Der React JSX-Code kann (und sollte) in getrennten Dateien ausgelagert werden. Dabei bietet sich webpack oder Browserify an.

React’s Konzept basiert auf Komponenten. Zum Beispiel:

React.renderComponent(
  React.DOM.h1(null, 'Hello, world!'),
  document.getElementById('example')
);

Oder mit JSX:

/** @jsx React.DOM */
React.renderComponent(
  <h1>Hello, world!</h1>,
  document.getElementById('example')
);

Das mischen von JavaScript und HTML sieht erstmal sehr eigenartig aus, wird aber nach einigen Beispielen immer schöner.

JSX – Javascript XML Syntax

Es sind nicht reale HTML-Elemente, sondern nur XML-ähnliche Darstellungen. Da es aber immerhin JavaScript ist, werden Attributnamen, die in XML nicht empfohlen werden, wie class oder for durch das JSX äquivalent className und htmlFor ersetzt (JSX Gotchas).

React JSX transformiert den XML-ähnlichen Syntax zu nativem JavaScript. Erkennt und transformiert werden aber nur Snippets, welche mit folgendem Kommentar beginnen:

/** @jsx React.DOM */

Beispiel für die Transformierung von XML zu JavaScript:

var AComponent;
// Input (JSX):
var app = <AComponent text="Hello" />;
// Output (JS):
var app = React.createElement(AComponent, {text:"Hello"});

Komponenten

Eigene Komponenten werden mit der createClass-Methode erstellt:

var AComponent = React.createClass({
    render: function(){
        return (
            <h1>Hello, world!</h1>
        );
    }
});

Die Komponente kann als erstes Argument in der bereits gezeigten renderComponent-Methode angegeben werden:

React.renderComponent(
    <AComponent/>,
    document.getElementById('example')
);

React macht aber mehr Spass, wenn den eigenen Komponenten Attribute (Props) hinzugefügt werden. Innerhalb der Komponente können diese mittels this.props verwendet werden:

var DayComponent = React.createClass({
    render: function(){
        return (
            <h1>Today is {this.props.day}!</h1>
        );
    }
});

React.renderComponent(
    <DayComponent day="Sunday" />,
    document.getElementById('example')
);

Komponenten Specs & Lifecycle

In React.createClass() muss eine Objekt-Spezifikation mitgegeben werden. Diese enthält neben der einzigen Pflichtangabe render, noch weitere optionale Methoden für die Komponentenspezifikation:

  • getInitialState: Die Rückgabe wird als anfänglicher State verwendet.
  • getDefaultProps:: Wenn props nicht verwendet werden, gelten diese als Fallback.
  • mixins: Ein Array von Objekten, um Funktionalität auf mehreren Komponenten zu bringen.

Und auch für den Komponenten Lifecycle:

  • componentWillMount: Wird auf client und server vor dem Rendern einmalig aufgerufen.
  • componentDidMount: Wird nur auf dem client nach dem Rendern einmalig aufgerufen. An diesem Punkt, hat die Komponente bereits die DOM-Repräsentation auf welche mit this.getDOMNode() zugegriffen werden kann.
  • shouldComponentUpdate: Soll die Komponente aktualisiert werden? Wird vor dem rendern aufgerufen, wenn sich props oder state ändert.
  • componentWillUpdate: Direkt vor dem rendern.
  • componentDidUpdate: Direkt nach dem rendern – Um Operationen am DOM direkt nach dem Update durchzuführen.
  • componentWillUnmount: Wird vor dem unmounten der Komponente vom DOM aufgerufen – soll für das Aufräumen von DOM Elementen, die in componentDidMount erstellt wurden, verwendet werden.

Es gibt auch noch weitere Methoden, detailliertere Informationen dazu in der React Component Specs Doku.

State

Der anfängliche State wird mit getInitialState gesetzt.
Mit der setState-Methode wird dieser aktualisiert und triggert dabei ein UI-Update.

var DayComponent = React.createClass({
    getInitialState: function(){
        return {
            day: "Tuesday"
        }
    },
    render: function(){
        return (
            <h1>{this.state.day}</h1>
        )
    }
});

Events

React verwendet SyntheticEvent, ein Cross-Browser Wrapper um die Nativen Browser-Events. Mit dem Selben Interface wie die nativen Events (stropPropagation(), preventDefault, …) sollen sie in allen Browsern identisch funktionieren.

Events werden in React den Komponenten als Eigenschaften hinzugefügt und triggern beim Auslösen eine in der Objektspezifikation definierte Methode:

var DayComponent = React.createClass({
    getInitialState: function(){
        return {
            day: "Tuesday"
        }
    },
    setSunday: function() {
        this.setState({
            day: "Sunday"
        });
    },
    render: function(){
        return (
            <h1>{this.state.day}</h1>
            <button onClick={this.setSunday}>Set sunday</button>
        )
    }
});

Data Flow

Im Gegensatz zum two-way-binding von Frameworks wie Angular, fließen die Daten bei React ganz wie nach Von Neumann model of computing unidirektional vom owner zum child. Das heißt, dass die Elternkomponente den State der ganzen Kette unter sich via this.props weitergeben muss.

Selbst wenn ein two-way-binding in React möglich ist, sollte es vorsichtig verwendet werden (zum Beispiel bei Formularen – wenn sich der State nach Eingabe des Users anpassen muss).

Um dieses Szenario abzubilden, würde React auf ein change-Event hören, darin die Informationen (normalerweise) vom DOM auslesen und es der Komponente mit setState() übergeben.

/** @jsx React.DOM */

var DayComponent = React.createClass({
  getInitialState: function(){
     return {
       initialItems: [
         "Monday",
         "Tuesday",
         "Wednesday",
         "Thursday",
         "Friday",
         "Saturday",
         "Sunday"
       ]
     }
  },
  componentWillMount: function(){
    this.setState({items: this.state.initialItems})
  },
  render: function(){
    return (
      <div>
        <DayList items={this.state.items}/>
      </div>
    );
  }
});

var DayList = React.createClass({
  render: function(){
    return (
      <ul>
      {
        this.props.items.map(function (item) {
          return <li>{item}</li>
        })
      }
      </ul>
    )  
  }
});

React.renderComponent(<DayComponent/>, document.getElementById('example'));

Aufgepasst: Innerhalb der map-Funktion bezieht sich this nicht mehr auf die Komponente, sondern auf die übergebene anonyme Funktion und somit window. Wenn jedoch ein click-Event auf eine in der Komponente definierte Methode gebunden werden soll, so kann der Kontext mit .bind(this) am Ende der anonymen Funktion, oder mit setzen von einer Variable _this ausserhalb gemacht werden:

handleDayClick: function(i) {
  console.log(this.props.items[i]);
},
render: function(){
  return (
    <ul>
    {
      this.props.items.map(function (item, i) {
        return (
          <li onClick={this.handleDayClick.bind(this, i)}>
            {item}
          </li>
        )
      }.bind(this))
    }
    </ul>
  )  
}

Oder mit _this:

render: function(){
  var _this = this;
  return (
    <ul>
    {
      this.props.items.map(function (item, i) {
        return (
          <li onClick={_this.handleDayClick.bind(this, i)}>
            {item}
          </li>
        )
      })
    }
    </ul>
  )  
}

Fazit

Es gibt viele gute Javascript Frameworks und viele weitere, noch bessere werden kommen. Vor ein paar Jahren war es noch nicht denkbar, eine komplexe, große Webanwendung komplett in Javascript zu entwickeln. Gerade Isomorphe Frameworks wie React, welche auf Client und Server laufen können, lösen diese Denkweise komplett auf. Und solange Google nicht komplett Javascript interpretieren kann (will), bleibt keine andere Möglichkeit als auf dem Server zu rendern und auf dem Client zu erweitern.

Dieser Überblick soll ein paar Grundsätze von React zeigen, es gehört aber sehr viel mehr dazu.

React: 1 Stern2 Sterne3 Sterne4 Sterne5 Sterne 4,88 von 5 Punkte, 16 abgegebene Stimmen.
Loading ... Loading ...

About the Author

Roberto Bez ist passionierter Webentwickler und TechLead bei der HolidayCheck AG. Für Roberto bedeutet das Entwickeln nicht nur Arbeit, sondern auch Freude, Motivation und täglich neue, aufregende Herausforderungen. Besonders gerne setzt er sich mit neuen Webtechnologien sowie Datenbanken aller Art auseinander und versucht diese in die tägliche Anwendungsentwicklung miteinzubringen. Neben dem Entwickeln trifft man ihn gerne Abends beim Laufen oder im Sommer bei Mountainbike-Touren durch die schönen Berge Südtirols.