DejaComponentBase
Basiskomponente, die automatisch neu rendert, wenn die
Query<T>- und Mutation<T>-Instanzen in ihrem Besitz
den Zustand wechseln. Deklariere sie als Felder oder Properties; die Basis heftet sich
bei der Initialisierung an jede an und löst sich beim Dispose.
public abstract class DejaComponentBase : ComponentBase, IAsyncDisposableMember#
ComponentToken#
Ein Token, das abgebrochen wird, wenn diese Komponente disposed wird. Die Queries und
Mutations der Komponente verwenden es bereits — reiche es nur an Arbeit weiter, die
Deja nicht für dich ausführt, etwa einen direkten API-Aufruf aus einem Event-Handler.
Es gibt nichts zu disposen, und ein Zugriff nach dem Dispose liefert ein bereits
abgebrochenes Token, statt zu werfen — eine späte Continuation sieht also einen
Abbruch statt einer ObjectDisposedException.
Observe<TState>#
Heftet state an diese Komponente: rendert bei jeder Änderung neu, reicht
ihm den registrierten DejaClient und das ComponentToken
dieser Komponente und löst sich beim Dispose.
Wird selten direkt aufgerufen. Die Discovery führt das bei der
Initialisierung für jede Query<T> /
Mutation<T> in einem Feld oder einer Property aus, was die
gewöhnliche Komponente abdeckt. Rufe es nur von Hand auf — für Zustand, den die
Discovery nicht sehen kann: nach der Initialisierung erstellt (ein Event-Handler,
eine Liste pro Zeile) oder in einer Komponente, deren
OnInitialized-Override base.OnInitialized() auslässt. Nicht
angehefteter Zustand fetcht trotzdem; er rendert die Komponente nur nie neu, erreicht
nie den gemeinsamen Cache und wird beim Dispose nie abgebrochen.
Gibt state zurück, damit es inline verwendet werden kann; dieselbe
Instanz zweimal anzuheften ist ein No-op. Wirft
InvalidOperationException, wenn eine andere Komponente den Zustand
bereits besitzt — gib dieser Komponente ihre eigene Instanz oder reiche die Daten als
[Parameter] nach unten. Ausgearbeitete Beispiele findest du in
Komponenten-Basisklasse.
OnInitialized#
Heftet den deklarierten Zustand an. Ein Override in einer abgeleiteten Komponente muss
base.OnInitialized() aufrufen — bevor der eigene Zustand verwendet wird,
üblicherweise in der ersten Zeile — sonst wird nichts angeheftet, und die Komponente
rendert bei Zustandsänderungen nie neu.
Dispose (hook)#
Überschreibe dies für synchrones Aufräumen; läuft während des Disposals, vor dem
DisposeAsync-Override und bevor die Basis sich vom beobachteten Zustand
löst. Rufe base.Dispose() aus einem Override auf.
DisposeAsync (hook)#
Überschreibe dies für asynchrones Aufräumen; läuft nach dem
Dispose-Override, bevor die Basis sich löst. Rufe
base.DisposeAsync() aus einem Override auf. Deklariere kein
@implements IAsyncDisposable — die Basis implementiert es bereits,
und dieses Override ist ihr Erweiterungspunkt.
Reihenfolge beim Disposal#
Wenn Blazor die Komponente entfernt, geht die Basis so vor:
- Sie bricht
ComponentTokenab, sodass laufende Ausführungen den Abbruch beobachten, solange sie es noch können; - sie führt den abgeleiteten
Dispose()-Hook aus, dann denDisposeAsync()-Hook — Overrides sehen ihren Zustand dabei noch angeheftet; - sie löst sich von allem beobachteten Zustand und disposed die Queries, die die Komponente erstellt hat — samt Abbruch ihrer Fetches — selbst wenn abgeleitetes Aufräumen wirft.
Leitplanke: neu deklariertes Disposal wirft#
Eine abgeleitete Komponente, die ihr eigenes Dispose oder
DisposeAsync deklariert (typischerweise über
@implements IDisposable /
@implements IAsyncDisposable), wird im Konstruktor mit einer
InvalidOperationException abgewiesen. Ein neu deklariertes
DisposeAsync würde Dejas Disposal vollständig ersetzen — die Komponente
würde ihre Anheftungen stillschweigend leaken und ihre Queries nie disposen; ein neu
deklariertes Dispose würde von nichts mehr aufgerufen, weil Blazor nur über
IAsyncDisposable disposed, sobald es vorhanden ist. Explizite
Interface-Reimplementierungen werden ebenfalls erkannt; Helfer im Stil von
Dispose(bool) sind erlaubt. Die Fehlermeldung nennt die Lösung: die Hooks
per protected override überschreiben.
Leitplanke: ausgelassenes base.OnInitialized()#
Wer base.OnInitialized() auslässt, bekommt Code, der kompiliert und einmal
rendert — dann hört die Komponente stillschweigend auf zu reagieren, weil nichts
angeheftet wurde. Deja erkennt das beim ersten Render und meldet es einmalig als
Konsolenfehler (über stderr, auf WebAssembly als console.error sichtbar),
unter Nennung der Komponente und der Lösung.
Discovery-Details#
- Felder und Properties werden einmal bei der Initialisierung gescannt, entlang der Typhierarchie.
[Parameter]-,[CascadingParameter]- und[Inject]-Member werden übersprungen — Parameter gehören dem Parent, der sie übergeben hat; ein Anheften würde sowohl den Listener-Slot des Parents stehlen (und werfen) als auch Zustand disposen, der der Komponente nicht gehört.- Zustand, der nach der Initialisierung zugewiesen wird, wird vom Scan nie erfasst —
bei späteren Renders wird nichts erneut gescannt — er muss also an
Observe()übergeben werden. - Der Scan ist trimming-annotiert und übersteht daher veröffentlichte, getrimmte WebAssembly-Builds.