Eigene native Views als Custom Components synchron rendern
Fabric ersetzt die asynchrone Bridge durch einen gemeinsamen C++ Shadow Tree, den JavaScript und native Seite direkt über JSI teilen. Wer eine eigene native View als Custom Component bauen will, muss verstehen, wie ComponentDescriptor, ShadowNode und generierter Codegen-Code zusammenspielen, von der ersten Spec-Datei bis zur fertigen ComponentView auf iOS und Android.
Inhaltsverzeichnis
- 1. Von der asynchronen Bridge zur synchronen Fabric-Pipeline
- 2. ComponentDescriptor und ShadowNode: die Bausteine einer Fabric-Komponente
- 3. Von der TypeScript-Spec zum generierten Fabric-Interface
- 4. Eigene Fabric-View auf iOS: von ComponentDescriptor zu ComponentView
- 5. Eigene Fabric-View auf Android: ViewManager trifft ComponentDescriptor
- 6. Synchrones State-Management ohne Round-Trip über die Bridge
- 7. Event-Pipeline: wie Interaktionen synchron durch Fabric wandern
- 8. Migration von altem View-Manager-Code: der Interop-Layer als Brücke
- 9. Performance-Charakteristik und Debugging von Fabric-Komponenten
- 10. Zusammenfassung
- 11. FAQ
1. Von der asynchronen Bridge zur synchronen Fabric-Pipeline
In der alten React Native Architektur lief jede Kommunikation zwischen JavaScript und nativem Code über die Bridge: ein asynchroner Kanal, der Nachrichten als JSON serialisierte, batchte und in beide Richtungen über eine Message Queue schickte. Für das Rendering bedeutete das, dass JavaScript einen Shadow Tree beschrieb, dieser über die Bridge an die native Seite geschickt wurde, dort Layout mit Yoga berechnet und erst danach die eigentlichen nativen Views erzeugt oder aktualisiert wurden. Jeder Schritt dieser Kette kostete Zeit für Serialisierung und Thread-Wechsel, was bei komplexen Listen oder häufigen Updates spürbar wurde.
Fabric ersetzt diese Kette durch eine gemeinsame C++ Kernschicht, die von JavaScript und nativem Code über JSI direkt angesprochen wird. Der Shadow Tree existiert nur noch einmal, in C++, und sowohl die JavaScript Runtime als auch iOS und Android greifen über Referenzen darauf zu, statt Kopien über eine Bridge zu verschicken. Dadurch werden Layout-Berechnung und Commit-Phase synchron möglich, was insbesondere für Custom Components wichtig ist, die exakt wissen müssen, wann eine neue Version ihres Shadow Nodes tatsächlich auf dem Bildschirm angekommen ist.
2. ComponentDescriptor und ShadowNode: die Bausteine einer Fabric-Komponente
Jede Fabric-Komponente besteht aus mindestens drei C++ Klassen: einem ShadowNode, der den unveränderlichen, immutable Zustand einer Komponenteninstanz im Shadow Tree hält, einem ComponentDescriptor, der Fabric mitteilt, wie ein ShadowNode erzeugt, geklont und mit Props versorgt wird, sowie einer Props-Klasse, die typisierte, aus JavaScript kommende Eigenschaften abbildet. Diese drei Bausteine sind komplett getrennt von der eigentlichen Plattform-View, was bewusst so entworfen ist: Der Shadow Tree kennt nur Layout und Props, nicht UIKit oder das Android View System.
Erst die ComponentView auf iOS beziehungsweise der entsprechende Fabric-View-Adapter auf Android übersetzt den ShadowNode in eine sichtbare native View. Diese Trennung erlaubt es, denselben ShadowNode theoretisch für unterschiedliche Rendering-Ziele wiederzuverwenden, etwa für Off-Screen-Rendering oder Tests ohne echte UI, was mit dem alten View-Manager-Modell praktisch nicht möglich war.
3. Von der TypeScript-Spec zum generierten Fabric-Interface
Eine eigene Fabric-Komponente beginnt nicht mit C++, sondern mit einer TypeScript-Spezifikationsdatei, die Codegen als Vertrag zwischen JavaScript und nativer Seite liest. Darin werden Props, Events und der Komponententyp deklariert, üblicherweise über codegenNativeComponent, und Codegen erzeugt daraus zur Buildzeit die passenden C++ Header für Props-Klassen, ObjC-Interfaces für iOS und Java-Interfaces für Android.
Der große Vorteil gegenüber der alten View-Manager-API ist, dass Typfehler zwischen JavaScript und nativer Seite bereits beim Build auffallen, nicht erst zur Laufzeit als stiller Crash oder falsch interpretiertes Prop. Wer eine neue Property hinzufügt, ändert nur die Spec-Datei, lässt Codegen laufen und bekommt sofort sichtbar, welche nativen Dateien angepasst werden müssen, weil der generierte Code sich entsprechend ändert.
4. Eigene Fabric-View auf iOS: von ComponentDescriptor zu ComponentView
Auf iOS implementiert eine Fabric-Komponente eine Unterklasse von RCTViewComponentView, die in updateProps die vom Shadow Tree übergebenen, typisierten Props entgegennimmt und in native UIKit-Eigenschaften übersetzt. Anders als beim alten View-Manager-Ansatz, bei dem Properties einzeln über Key-Value-Paare gesetzt wurden, kommt hier ein vollständiges, immutable Props-Objekt an, das per Vergleich mit dem vorherigen Zustand effizient auf tatsächliche Änderungen geprüft werden kann.
Registriert wird die Komponente über eine statische supportedViewConfigCls-Methode und eine Fabric-ComponentDescriptorProvider-Registrierung, die beim App-Start in die globale Component Registry eingetragen wird. Fehlt diese Registrierung, fällt Fabric für die betroffene Komponente kommentarlos auf eine leere View zurück, ein Debugging-Fallstrick, der in der Praxis regelmäßig zu Verwirrung führt.
5. Eigene Fabric-View auf Android: ViewManager trifft ComponentDescriptor
Auf Android bleibt der klassische ViewManager als Bindeglied erhalten, wird aber um eine Fabric-spezifische Schicht ergänzt: Der generierte ViewManagerDelegate übernimmt das Mapping der Props aus dem generierten Java-Interface auf konkrete Setter-Methoden, sodass die eigentliche ViewManager-Klasse nur noch die Setter implementieren muss, ohne sich um Reflection oder manuelles Prop-Parsing zu kümmern.
Zusätzlich braucht jede Fabric-fähige Android-Komponente einen Eintrag in der ReactPackage, der sowohl den ViewManager als auch den passenden ComponentDescriptor-Namen liefert, damit die native Runtime beim Aufbau des Shadow Tree weiß, welche C++ ComponentDescriptor-Implementierung für einen gegebenen Komponentennamen zuständig ist. Ein falsch geschriebener Name führt auch hier zu einer stillschweigend leeren View statt einer klaren Fehlermeldung.
6. Synchrones State-Management ohne Round-Trip über die Bridge
Ein zentrales Feature von Fabric ist die Möglichkeit, nativen State direkt im Shadow Tree zu halten, statt jede Zustandsänderung über einen kompletten React-Re-Render-Zyklus zurück nach JavaScript zu schicken. Über die generierte State-Klasse kann eine native Komponente, etwa ein Scroll-Offset oder eine Textmessung, ihren Zustand direkt committen, und Fabric sorgt dafür, dass dieser State konsistent mit dem nächsten Layout-Pass verrechnet wird.
Das ist besonders bei Komponenten wichtig, die häufig und mit hoher Frequenz Zustand ändern, etwa ein natives Textfeld mit Cursor-Position oder eine Scroll-View mit Momentum-Scrolling. Ohne diesen Mechanismus müsste jede Positionsänderung erst durch die JavaScript-Ebene laufen, bevor sie im Shadow Tree ankommt, was bei 60 oder 120 Hertz Eingabefrequenz spürbare Verzögerungen verursachen würde.
7. Event-Pipeline: wie Interaktionen synchron durch Fabric wandern
Events wie Touches oder Layout-Änderungen werden in Fabric über eine dedizierte EventEmitter-Instanz je ShadowNode ausgelöst, die C++ seitig priorisiert und gebündelt an die JavaScript Runtime weitergereicht wird. Wichtig für Custom Components ist, dass Events, die als direct markiert sind, ohne den Umweg über die klassische Bubbling-Logik des alten Systems direkt an den registrierten JavaScript-Handler gehen.
Die Spec-Datei legt über den Event-Typ fest, ob ein Event synchron priorisiert wird, etwa für kontinuierliches Feedback wie Drag-Gesten, oder als niedrig priorisiertes, diskretes Event behandelt werden darf. Wer diese Priorisierung falsch wählt, riskiert entweder unnötigen Rendering-Druck bei trivialen Events oder spürbares Lag bei zeitkritischen Interaktionen wie einem Slider.
8. Migration von altem View-Manager-Code: der Interop-Layer als Brücke
Für Bibliotheken, die noch keine native Fabric-Komponente besitzen, existiert der Fabric Interop Layer, der alte View-Manager-basierte Komponenten automatisch in eine kompatible Fabric-Hülle einpackt. Das erlaubt einen schrittweisen Umstieg, bei dem nicht sofort jede Drittanbieter-Komponente neu geschrieben werden muss, kostet aber einen Teil der Performance-Vorteile, weil intern weiterhin über den alten Prop-Diffing-Mechanismus gearbeitet wird.
Für eine echte Migration lohnt es sich, zuerst die Props- und Events-Spec zu formulieren, dann die generierten Interfaces zu implementieren und erst am Ende den alten ViewManager-Code zu entfernen, statt beides parallel zu pflegen. In der Praxis zeigt sich, dass der größte Aufwand nicht im C++ Teil liegt, sondern in der sauberen Migration von imperativen Befehlen, die früher über UIManager.dispatchViewManagerCommand liefen und jetzt eigene, typisierte Commands in der Spec brauchen.
9. Performance-Charakteristik und Debugging von Fabric-Komponenten
Der spürbarste Performance-Effekt von Fabric zeigt sich bei Listen mit vielen gleichzeitig sichtbaren Custom Components, weil Layout-Berechnung und Commit synchron und ohne Bridge-Serialisierung ablaufen, was Ruckler beim schnellen Scrollen deutlich reduziert. Für eigene Komponenten lohnt sich ein Blick auf die Anzahl der Re-Renders im Shadow Tree, die sich über Flipper oder das eingebaute Fabric-Logging beobachten lässt.
Klassische Fehlerquellen sind vergessene ComponentDescriptor-Registrierungen, die zu leeren Views ohne Fehlermeldung führen, sowie Abstürze durch nicht threadsichere Zugriffe auf UIKit- oder Android-View-Objekte aus einem Hintergrund-Thread heraus, da Fabric Layout-Berechnungen bewusst außerhalb des Hauptthreads durchführt. Wer eine eigene Fabric-Komponente debuggt, sollte deshalb zuerst prüfen, ob UI-Mutationen tatsächlich auf dem Main Thread landen, bevor er tiefer in die C++ Schicht einsteigt.
| Aspekt | Alte Bridge-Architektur | Fabric | Auswirkung für Custom Components |
|---|---|---|---|
| Datenaustausch | Asynchrone JSON-Serialisierung über die Bridge | Direkter C++ Zugriff über JSI, kein Serialisieren | Weniger Latenz bei Props- und State-Updates |
| Shadow Tree | Getrennte Kopien in JS und nativ | Eine gemeinsame C++ Instanz | Konsistenter Zustand ohne Synchronisationsfehler |
| Layout-Commit | Asynchron, oft mit sichtbarem Versatz | Synchron im gleichen Renderzyklus | Weniger Layout-Flackern bei dynamischen Views |
| Prop-Typisierung | Manuell, Laufzeitfehler bei Tippfehlern | Codegen-generiert, Buildzeit-geprüft | Frühere Fehlererkennung bei neuen Props |
| Imperative Befehle | dispatchViewManagerCommand mit String-Namen | Typisierte Commands aus der Spec | Weniger Laufzeitabstürze durch falsche Commandnamen |
| Registrierung | View-Manager-Klasse mit manuellem Setup | ComponentDescriptor-Provider in globaler Registry | Fehlende Registrierung fällt frühzeitig als leere View auf |
| Debugging-Werkzeug | Chrome DevTools ohne native Sicht | Flipper mit Fabric-spezifischem Logging | Re-Renders und Commits im Shadow Tree direkt sichtbar |
Mironsoft
React-Native-App-Entwicklung und Magento-Anbindung
Eine mobile App zum Magento-Shop, die wirklich rund läuft?
Wir entwickeln React-Native-Apps, die sauber an die Magento REST- oder GraphQL-API angebunden sind, von der ersten Codezeile bis zur Veröffentlichung im App Store und bei Google Play.
App-Konzeption
Architektur und Feature-Umfang einer Magento-angebundenen App gemeinsam planen.
Magento-API-Integration
Produktkatalog, Warenkorb und Checkout sauber an die Shop-API anbinden.
Store-Veröffentlichung
App Store- und Google-Play-Freigabeprozess ohne Stolperfallen begleiten.
10. Zusammenfassung
Fabric Custom Components: Das Wichtigste auf einen Blick
ShadowNode & ComponentDescriptor
Bilden das C++ Fundament jeder Fabric-Komponente, komplett getrennt von der sichtbaren Plattform-View.
Codegen
Erzeugt aus einer TypeScript-Spec typisierte Interfaces für iOS und Android, Fehler fallen beim Build statt zur Laufzeit auf.
Synchroner State
Erlaubt nativen Komponenten, häufigen Zustand ohne Round-Trip durch JavaScript direkt im Shadow Tree zu committen.
Interop Layer
Erleichtert den schrittweisen Umstieg alter View-Manager-Komponenten, kostet dabei aber einen Teil der Performance-Vorteile.