Der Fabric-Renderer: eigene native Views als Custom Components bauen
AI generated
RN
native
React Native / New Architecture
Der Fabric-Renderer
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.

11 Min. Lesezeit Fabric ShadowNode ComponentDescriptor

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.

11. FAQ: Fabric Custom Components: Das Wichtigste auf einen Blick

1Was ist der Unterschied zwischen einem ShadowNode und einer ComponentView?
Der ShadowNode ist eine immutable C++ Repräsentation im Shadow Tree, die nur Props und Layout kennt. Die ComponentView ist die tatsächliche native Plattform-View, die aus dem ShadowNode erzeugt wird.
2Muss ich für eine eigene Fabric-Komponente C++ schreiben?
Direkt meist nicht, da Codegen die C++ Interfaces aus der TypeScript-Spec generiert. Die eigentliche Implementierung erfolgt in Swift/Objective-C++ auf iOS und Kotlin/Java auf Android.
3Was passiert, wenn ich die ComponentDescriptor-Registrierung vergesse?
Fabric rendert dann kommentarlos eine leere View statt eines Fehlers, was in der Praxis ein häufiger Debugging-Fallstrick ist.
4Kann ich alte View-Manager-Komponenten weiter verwenden?
Ja, über den Fabric Interop Layer, der sie automatisch in eine kompatible Hülle einpackt, allerdings mit geringeren Performance-Vorteilen als eine native Fabric-Komponente.
5Wie unterscheidet sich State in Fabric von React-State?
Fabric-State wird direkt im Shadow Tree committet und muss nicht erst einen kompletten Round-Trip durch JavaScript durchlaufen, was ihn für hochfrequente Änderungen deutlich schneller macht.
6Wo lege ich fest, ob ein Event synchron priorisiert wird?
In der TypeScript-Spec-Datei über den Event-Typ, der Fabric mitteilt, ob ein Event kontinuierliches Feedback wie eine Geste oder ein diskretes, niedriger priorisiertes Ereignis ist.
7Warum stürzt meine Fabric-Komponente bei UI-Updates ab?
Meist wegen nicht threadsicherer Zugriffe auf UIKit- oder Android-View-Objekte aus einem Hintergrund-Thread, da Fabric Layout-Berechnungen bewusst außerhalb des Hauptthreads durchführt.
8Sind imperative Commands in Fabric noch möglich?
Ja, aber typisiert über die Spec-Datei statt über generische String-basierte dispatchViewManagerCommand-Aufrufe wie in der alten Architektur.
9Lohnt sich eine Fabric-Migration für kleine, seltene Custom Components?
Oft nicht sofort, weil der Interop Layer für seltene, unkritische Komponenten ausreichend Kompatibilität bietet, während der Migrationsaufwand für stark frequentierte Komponenten wie Listen-Items klarer lohnt.
10Welches Tool eignet sich am besten zum Debugging von Fabric-Komponenten?
Flipper zusammen mit dem eingebauten Fabric-Logging zeigt Re-Renders und Commit-Zyklen im Shadow Tree und hilft, unnötige Updates in eigenen Komponenten aufzuspüren.