React
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.
- Part 1: Einführung in React
- Part 2: Server Side Rendering
- Part 3: React Flux Architektur
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.
Hi Robert,
haha, lustiger Vergleich.
Mich würde noch ein Vergleich zu anderen isomorphen Frameworks wie Rendr interessieren.
Bin auf den nächsten Artikel gespannt.
Grüße
Micha
Hi Micha,
danke für den Kommentar!
Steht auf der ToDo Liste.
Hey Roberto,
hab mich Heute zum ersten mal mit React beschäftigt und bin ziemlich schnell auf deinen Blog gestoßen. Hast alles super erklärt! Aber mir sind ein paar Kleinigkeiten beim durcharbeiten deines Blogs bei den Codebeispielen aufgefallen.
Unter „Events“ müssen die Elemente & mit einem Wrapper umschlossen werden, sonst wird es nicht in valides JS umgewandelt!
Beispiel:
Anders bekommt man das in der Console zurück: Uncaught Error: Parse Error: Line 15: Adjacent JSX elements must be wrapped in an enclosing tag
Und unter „Data Flow“ hat es bei mir erst funktioniert, als ich React.renderComponent() durch React.render() ersetzt habe.
Gruß Dan
PS: Hoffe ich bin dir nicht zur nahe getreten, finde den Artikel echt klasse, hat mir weiter geholfen!