Top 3 React Features für 2019

Keine Kommentare

Im November 2018 besuchte ich den React Day Berlin und habe am React Advanced Workshop teilgenommen. Meine persönlichen Highlights stelle ich in diesem Artikel vor.

Hooks, Hooks und nochmal Hooks

Am Ende der Konferenz war es ein Running Gag bei jedem Talk: Wenn man in seinem Vortrag nicht das Wort „Hooks“ verwendet hatte, war man out! Und ich muss sagen, dass mir Hooks auch wirklich sehr gut gefallen. Was sind Hooks? – Es sind eine Handvoll vordefinierter Funktionen in React, mit denen sich Dinge wie State, Seiteneffekte, Referenzen oder Context etc. sehr elegant und vor allem wiederverwendbar nutzen lassen. Anders ausgedrückt: Man kann mit Hooks nun alles in funktionalen Komponenten umsetzen, was vorher nur mit Klassen möglich war.

Kleines Beispiel: Wenn man eine Komponente mit State ausstatten wollte, musste man bisher Folgendes machen:

Man braucht also eine Klasse, um die state-Variable zu definieren, und man muss die Methode setState() aufrufen, um den State zu ändern. Das funktioniert eigentlich ganz gut. Wie sieht das ganze jetzt mit Hooks aus? Voilà:

Wir haben eine funktionale Komponente und rufen zu Beginn die Funktion useState() auf. useState() ist der Hook. Als Parameter setzt man den initialen Wert. Erlaubt sind alle primitiven Werte sowie Arrays und Objekte. In unserem Fall starten wir bei 0. Als Rückgabewert bekommen wir ein Array mit zwei Elementen zurück, welches wir mittels Array-Destrukturierung zerlegen. Das erste Element ist der aktuelle Wert des States, also am Anfang die 0. Das zweite Element ist eine Funktion, um den State neu zu setzen, ähnlich der setState()-Methode in der oberen Klasse. Der Rest ist dann selbsterklärend.

Wo ist nun der Vorteil? – Zum einen brauchen wir keine Klassen mehr. Yay! Die Syntax ist kompakter, und wir können die Variablen für unseren State sowie der Funktion, die den State verändert, selbst wählen. count und setCount sind hier beliebige Beispiele. Gerade bei vielen State-Variablen in einer Komponente ist das besser lesbar als mit der setState-Variante. Aber der wichtigste Vorteil ist, dass wir den gesamten Code zum State-Management auch in eine eigene Datei refaktorieren und wiederverwenden können. Und genau das macht momentan die Community und hat bereits eine Unzahl von sog. „Custom Hooks“ erstellt: https://nikgraf.github.io/react-hooks

Ein weiterer sehr praktischer Hook ist useEffect(). Übrigens: Alle Hook-Namen – auch die selbst erstellten – fangen per Konvention mit „use…“ an. Das macht es leichter, sie zu erkennen. Mittels useEffect() werden Seiteneffekte umgesetzt. Das heißt, wir führen immer dann eine Funktion aus, wenn bestimmte äußere Änderungen eintreten. Hier ein Beispiel:

Dieser Code ist bereits in eine eigene Datei ausgelagert, sodass wir ihn in verschiedenen Komponenten wiederverwenden können. Um ihn zu verwenden, rufen wir einfach die Funktion useWindowMeasure() in einer Komponente auf.

useEffect(...) hat zwei Parameter: Der erste ist die Funktion, die ausgeführt werden soll. Der zweite ist ein Array von Werten. Sobald sich in dem Array einer der Werte ändert, wird die Funktion erneut ausgeführt. In unserem Fall gebe ich ein leeres Array rein. Das führt dazu, dass unsere Funktion nur einmalig beim Mounten der Komponente ausgeführt wird. Das simuliert also die Lifecycle-Methode componentDidMount() und das, obwohl wir keine Klassen für unsere Komponente nutzen müssen. Wenn der erste Parameter eine Funktion als Rückgabewert besitzt, wie in dem Beispiel hier (return () => { window.removeEventListener('resize', handler)}), dann wird diese beim Unmount der Komponente aufgerufen. Das ist dann wie componentWillUnmount().

Mehr Details zu diesen beiden Hooks sind hier zu finden:
https://reactjs.org/docs/hooks-state.html
https://reactjs.org/docs/hooks-effect.html

Leider sind Hooks noch nicht offiziell supported. Wahrscheinlich werden sie mit React 16.8 (aktuell haben wir 16.7) released werden. Mittels folgendem Import in der package.json könnt ihr jetzt schon damit spielen:

"dependencies": {
"react": "^16.7.0-alpha.2",
"react-dom": "^16.7.0-alpha.2"
}

Context API

Eigentlich ist das Context API bereits seit React 16.3 (März 2018) offiziell nutzbar. Wer sich damit bisher nicht beschäftigt hat und stattdessen Redux oder MobX etc. einsetzt, sollte jetzt mal einen Blick darauf werfen. Es gibt übrigens auch einen Hook, der die Arbeit damit erleichtert 😉

Selbst Redux und MobX arbeiten unter der Haube seit eh und je mit dem Context API, um den globalen State darin zu verwalten und ihn den Komponenten zur Verfügung zu stellen. Und eines vorneweg: Das Context API ersetzt Redux etc. noch nicht komplett, jedoch ist es gerade für kleine Anwendungen meist völlig ausreichend. Der Vorteil von Redux ist z. B., dass durch die indirekte State-Manipulation über Actions und Reducer Dinge ermöglicht werden wie Zeitreisen durch die Historie der State-Änderungen, persistieren und rebooten des gesamten States und andere Features (siehe dazu https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367)

Das Context API ist dagegen jedoch sehr simpel. Es funktioniert wie folgt:

Wir erstellen mittels React.createContext() ein neues Context-Objekt mit einem initialen Wert als Parameter. Der Wert kann alles sein: Ein Primitive, ein Objekt, Array etc. Hier nutzen wir den String “dark”. Dann verwenden wir die Context-Provider-Komponente (<ThemeContext.Provider value="light">...</ThemeContext.Provider>) als Higher Order Component für alle Kind-Komponenten, die später auf den State zugreifen sollen. Hier also für Header und Footer. Dabei setzen wir den value des Providers auf den Wert, der nach unten gereicht wird. Man kann auch mehrere Provider ineinander schachteln. Zum Auslesen des States nutzen wir entweder eine Consumer-Komponente (<ThemeContext.Consumer>...</ThemeContext.Consumer>) oder den Hook useContext(). In beiden Fällen wird in der Komponenten-Hierarchie nach oben gegangen und der nächstgelegene Provider gesucht. Dessen Wert wird zurückgegeben. In unserem Fall ist es der String „light“.

Durch das Context API sparen wir uns das Weiterreichen von Props über mehrere Komponenten. Um den State zu aktualisieren gibt es leider keine Funktion wie z. B. ThemeContext.update('light') etc. Deswegen muss der State manuell z. B. innerhalb einer State-Variablen einer Komponente gespeichert werden:

React Portals

Portals sind auch nicht so neu (seit React 16), aber vor dem React Day hatte ich davon noch nichts gehört und das, obwohl man sie in fast jeder App gebrauchen kann. Im Prinzip rendern wir mit einem Portal eine Komponente außerhalb unseres Root-DOM-Elementes, in der wir die React App rendern. Das ist z. B. interessant für Modals, Dialoge, Overlays etc. Die Verwendung ist recht simpel:

Im HTML-Dokument ist dann nicht nur ein typisches <div id="root"></div>, sondern noch ein weiteres <div id="modal"></div> enthalten.

Fazit und Ausblick

Mit den vorgestellten Features kann man seine nächsten React-Projekte noch um einiges eleganter gestalten als zuvor. Zudem lassen sich die Features in bestehende Projekte einfügen, ohne dabei die existierenden Tools zu behindern. Ach, und Render Props wurden übrigens schon wieder als outdated beschrieben, da man mittels Hooks und dem Context API die meisten Anwendungsfälle sehr einfach abbilden kann. Dazu auch mehr hier: https://medium.com/@IvanChukitow/render-props-vs-react-hooks-c3cf0162ed7b

Das nächste Feature, das ich mir genauer anschauen würde, ist Suspense. Dieses ist jedoch erst für Mitte 2019 geplant. Schon jetzt kann man Suspense zusammen mit React.lazy() für Code Splitting nutzen. (https://reactjs.org/blog/2018/10/23/react-v-16-6.html)

René Bohrenfeldt

René Bohrenfeldt ist seit mehr als 10 Jahren in der Software-Entwicklung tätig und fokussiert sich dabei hauptsächlich auf Frontend-Technologien. Bei der codecentric AG setzt er als IT-Consultant sowohl sein Entwicklungs-Know-how als auch seine Kommunikationsstärke als PO und Moderator ein.
Darüber hinaus engagiert sich der studierte Betriebswirt unternehmerisch und ist Mitgründer einer Busvermietung in Berlin.

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.