Lifecycle · state · re-render

Livscyklus og state

Forstå hvornår Blazor kalder livscyklus-metoder, hvordan parametre opdateres, og hvornår UI re-renderes — afgørende når I loader Pokémon async eller skifter filter på en LoL-liste.

Læringsmål

Hvornår bruger I livscyklus-metoder? Når data afhænger af route/parametre (LoL /champion/{id}), når parent skifter [Parameter], eller når I skal undgå race conditions ved async API-kald fra OpgaverAPI.

Tjek dig selv før du går videre

  • Du kan undgå dobbelt-fetch ved første load.
  • Du ved hvornår OnParametersSetAsync kører igen.
  • Du kan nævne hvorfor CancellationToken bruges ved navigation.

En komponent er ikke «bare HTML» — den har en livscyklus fra første render til destruction. Når parent sender nye parametre (fx valgt champion-id), skal child enten reagere i OnParametersSet eller via bound properties.

Typisk rækkefølge

  1. SetParametersAsync — parent sender parametre.
  2. OnInitialized / OnInitializedAsync — én gang ved oprettelse (god til første API-kald).
  3. OnParametersSet / OnParametersSetAsync — hver gang parametre ændres (god til at re-loade detail når id skifter).
  4. Event handlers — bruger klikker «Hent næste side» → opdater state → render.
  5. Dispose — ryd timers, unsubscribe.

Regel: Init til data der kun hænger på siden (statisk intro). OnParametersSet til data der følger id/query fra URL. Events til bruger-initieret refresh («Hent igen»).

Livscyklus-lab

Tryk på trinene — loggen viser hvilke metoder der kører, og hvornår UI opdateres.

  1. OnInitializedAsync
  2. OnParametersSet
  3. Render
  4. Event (klik)
  5. Dispose
# Vælg et trin

OnInitializedAsync — første load

@page "/pokemon-lifecycle"
@inject IPokemonService Api

<h1>Starter Pokémon</h1>
<ul>@foreach (var p in mons) { <li>@p.Name</li> }</ul>

@code {
  List<Mon> mons = new();

  protected override async Task OnInitializedAsync()
  {
    mons = await Api.GetStarterAsync();
  }

  record Mon(string Name);
}

Brug async livscyklus-metoder til I/O. Vis loading-state hvis kaldet tager tid — ellers ser brugeren en tom liste.

Elev-fejl → symptom → fix: Data hentes i constructoren — symptom: HttpClient null eller ingen render. Fix: flyt til OnInitializedAsync.

OnParametersSet — route/query ændres

@* ChampionDetail.razor — [Parameter] public string Id *@
@code {
  Champion? champ;

  protected override async Task OnParametersSetAsync()
  {
    champ = await Lol.GetChampionAsync(Id);
  }
}

Når parent eller router sender nyt Id, kører OnParametersSetAsync igen — uden at komponenten nødvendigvis destrueres.

Hvornår OnParametersSet frem for kun Init? Master/Detail-Pokémon: parent sætter PokemonId ved klik — samme detail-komponent, nyt id. Router med @page "/pokemon/{id:int}" opfører sig tilsvarende.

StateHasChanged og rendering

Vend kortene

Klik for at afsløre — hvornår hvilken livscyklus-hook?

Efter I opdaterer felter uden for en normal event (fx callback fra timer eller JS interop), kan UI mangle opdatering indtil StateHasChanged() kaldes. I de fleste @onclick / @bind-flows sker det automatisk.

protected override bool ShouldRender()
{
    // Eksempel: skip render hvis kun intern debug-state ændrede sig
    return _visibleChanged;
}

Hvornår ShouldRender? Når en stor LoL-liste re-renderer for ofte (fx parent opdaterer et ur hvert sekund). Returnér false når jeres felter ikke påvirker markup — men brug det målrettet; forkert brug giver «frossen» UI.

EventCallback-timing: Parent opdaterer state i callback → child får nye parametre → OnParametersSet kan køre efter parent’s render. Hvis child også muterer samme state, kan I se et ekstra render-trin — derfor one-way data ned, events op.

Faldgrube: At hente tung data i OnParametersSet og OnInitialized uden guard giver dobbelt API-kald ved første load. Tjek om parametre allerede er sat, eller brug kun OnParametersSetAsync med «first render»-flag.

Cascading values

Layout kan levere fælles værdier (tema, API-base, bruger-context) uden at prop-drille gennem hver komponent:

@* App.razor / Layout *@
<CascadingValue Value="apiBase" Name="ApiBase">
  @Body
</CascadingValue>

@* Child *@
@code {
  [CascadingParameter(Name = "ApiBase")]
  public string ApiBase { get; set; } = "";
}

Hvornår cascading? Når mange dybe børn skal kende samme Mercantec Auth-kontekst, tema eller read-only config — undgå at prop-drille ApiBase gennem fem niveauer. Alternativ: inject IConfiguration eller dedikeret service.

Quiz 1

Hvor loader I typisk første gang data til en sidekomponent med @page?

Quiz 2

Parent ændrer [Parameter] public int PokemonId — hvilken metode skal re-loade detail?

CancellationToken ved navigation

Hvorfor det betyder noget: Eleven klikker Pikachu, skifter hurtigt til LoL-ruten — langsomt Pokémon-detail returnerer stadig og overskriver ny side. CancellationToken stopper unødigt arbejde og race conditions.

protected override async Task OnParametersSetAsync(CancellationToken cancellationToken)
{
    loading = true;
    try
    {
        detail = await Lol.GetChampionAsync(Id, cancellationToken);
    }
    catch (OperationCanceledException)
    {
        return; // bruger navigerede væk — ikke opdater state
    }
    finally { loading = false; }
}

Sådan fejler det i praksis: OnInitializedAsync og OnParametersSetAsync kalder begge API ved første load — Network viser to identiske GET. Fix: kun ParametersSet med guard, eller skip fetch i Init hvis Id allerede er sat.

private int _loadedId = -1;

protected override async Task OnParametersSetAsync(CancellationToken ct)
{
    if (Id == _loadedId) return;
    _loadedId = Id;
    detail = await Lol.GetChampionAsync(Id, ct);
}

Mini-opgave

Detail med [Parameter] int Id; log Init vs ParametersSet; eliminer dobbelt-fetch med guard.

Quiz 3

Navigation væk under API-kald — bedste praksis?

Læs hos Microsoft

Prøv med API

  • Lav Master/Detail: liste fra /go/pokemon, detail når PokemonId parameter skifter — observer Network for dobbelt-kald.
  • Sammenlign med LoL-demo: hvornår hentes data ift. navigation?
16476 #1416475 #7

Baseret på MAGS Blazor-wiki og Microsoft Learn — Blazor.