createContext
bereit:LanguageContext.Provider
sowie LanguageContext.Consumer
.DisplaySelectedLanguage
Komponente verwenden und haben dort immer den jeweils vom Provider bereitgestellten Wert verfügbar. Ändert sich der Wert im Provider, werden auch alle Consumer- Komponenten unterhalb des entsprechenden Providers mit dem aktualisierten Wert neu gerendert!DisplaySelectedLanguage
-Komponente übergeben, hat diese dennoch Kenntnis von der aktuell ausgewählten Sprache und zeigt korrekt an:value
einer Provider-Komponente, werden alle Consumer-Komponenten, die sich innerhalb dieses Providers befinden, neu gerendert!defaultLanguageContextValue
, welches den Default-Wert unseres neuen Context-Objekts darstellt. Dieses besteht aus:translationStore
, welches alle vorhandenen Übersetzungen beinhaltet.de
) , die in der language
Eigenschaft gespeichert wirdavailableLanguages
) mit allen verfügbaren Sprachen aus dem translationStore
-Objekt, das wir mittels Object.keys()
dynamisch aus den Eigenschaften auf erster Ebene erzeugen (in unserem Beispiel also ['de', 'en']
).changeLanguage()
), die später in der Localized
-Komponente durch eine echte Implementierung ersetzt wird. Dies dient dazu, dass wir bei inkorrekter Benutzung des Contexts nicht Gefahr laufen eine Funktion aufzurufen, die noch gar nicht existiert. In diesem Fall würde die Warnung ausgegeben werden „Funktion changeLanguage() nicht implementiert!“.changeLanguage()
-Funktion kann erst später in der Komponente selbst implementiert werden, da React ansonsten keine Möglichkeit hätte, mit Bordmitteln den State (also in diesem konkreten Fall die Sprache und die Übersetzungen) zu ändern, da State für React nur innerhalb einer Komponente existiert. So könnten wir zwar die aktuelle Spracheinstellung bspw. in einer globalen Variable speichern, React würde die Komponente dann bei einer Änderung aber nicht neu rendern, da sich weder Props noch State geändert haben, dies aber eine Bedingung ist, um React den Seitenbaum neu rendern zu lassen.Localized
-Komponente dient nun als Wrapper-Komponente für unseren neu erstellten Context. In ihr speichern (und ändern!) wir die vom Benutzer ausgewählte Sprache, indem wir den State entsprechend setzen. Wir speichern dazu das defaultLanguageContextValue
-Objekt im State der Komponente und implementieren hier zusätzlich die changeLanguage()
-Methode. Diese empfängt eine Sprache (also de
oder en
), modifiziert den State entsprechend, holt sich die Übersetzungen für die neu ausgewählte Sprache aus dem translationStore
Objekt und schreibt diese als translations
neu in den State. Wechselt der User bspw. die Sprache von Deutsch (Voreinstellung) zu Englisch, überschreibt die Funktion alle deutschen Übersetzungen im State mit den englischen Übersetzungen. Durch den Aufruf von this.setState()
wird ein Re-Rendering ausgelöst und alle Context-Consumer innerhalb des Komponenten-Baums werden mit dem aktualisierten Wert, den wir in der render()-Methode der Komponente an den Context-Provider übergeben, neu gerendert.changeLanguage()
-Methode zuerst implementiert. Dies hat den einfachen Grund, dass this.changeLanguage
ansonsten noch gar nicht definiert, also undefined
wäre. Um dies zu umgehen, definieren wir die Methode, bevor wir die state
-Eigenschaft der Klasse konstruieren.translations
-Objekt zugreifen zu können. Wir nennen diese Komponente Translated und als einzige Prop erhält sie die Eigenschaft, auf die wir im translations
-Objekt zugreifen wollen. In unserem konkreten Beispiel kann das also greeting
oder headline
sein.App
-Komponente sieht dann entsprechend so aus:Headline
und Greeting
Komponenten aus dem vorherigen Beispiel können wir uns dann einfach sparen.Translated
-Komponente key
zu nennen. So wäre das doch schön kurz und gut lesbar:key
ein reservierter Name in JSX ist und zur Identifizierung von Elementen dient, wenn diese Elemente in einem Array verwendet werden. Die genauen Gründe dazu sind im Kapitel über „Listen, Refs, Fragments und Conditional Rendering“ in „Die Grundlagen“ nachzulesen, dort konkret im Abschnitt „Listen“.contextType
ist hier das Stichwort. Dieser kann einer Klassen-Komponente in Form einer gleichnamigen statischen Eigenschaft zugewiesen werden, anschließend kann dann innerhalb der Komponente mittels this.context
auf den Wert des jeweiligen Contexts zugegriffen werden. Als Wert bekommt die contextType
-Eigenschaft einen Context zugewiesen, der zuvor mittels React.createContext()
erzeugt wurde.contextType
zu definieren und dieser einen Context zuzuweisen.contextType
-Eigenschaft der Komponente (die nun eine Klassen-Komponente und keine Function-Component mehr ist) als Wert unseren LanguageContext
zu und schon haben wir in this.context
den Wert des Contexts zur Verfügung.contextType
also außerhalb der Komponente definieren und nicht mehr innerhalb. Im Grunde genommen ist das aber letztendlich Geschmackssache und hat sonst keine Implikationen oder Nachteile. Es ist allein eine andere Syntax-Variante, die erst in späteren ECMAScript-Versionen oder eben durch Transpiling mittels Babel möglich ist. Möglich gemacht wird sie durch das Babel-Plugin @babel/plugin-proposal-class-properties
.render()
-Methode einer Komponente stets neu on-the-fly erzeugt wird. Daher empfiehlt es sich normalerweise, den Context-Wert außerhalb der render()-Methode zu erzeugen und eine Referenz zum Wert statt eines stets neu erzeugten Werts zu übergeben.{color: this.state.color}
, welches wir als Wert für den Context-Provider benutzen. Da React lediglich überprüft ob die Referenz zum entsprechenden value
im aktuellen render()
-Aufruf der aus dem vorherigen render()
-Aufruf entspricht, dies hier jedoch niemals der Fall ist, da ja ein neues Objekt an Ort und Stelle erzeugt wird, werden hier sämtliche Consumer-Komponenten neu gerendert.state
-Objekt der Komponente. Da diese auch beim Re-Rendering der Komponente erhalten bleibt, löst dieses Vorgehen kein Re-Rendering aus, solange sich der Inhalt des States der Komponente nicht ändert!