Search code examples
blazorblazor-client-sideblazor-component

Blazor component not calling OnInitializedAsync on statechange, chained data-fetching


Im new to blazor and still learning, and I have a state-problem with my dashboard.

I have one component that fetches the dashboard and the panels:

@if (Data == null)
{
    <Loader></Loader>
    return;
}
<Row NoGutters="true">
    @foreach (var panel in Data.Panels)
    {
        <Column XS="12" SM="12" MD="6" LG="6" XL="4" Class="mb-4">
            <CascadingValue Value="@panel.Id">
                <DashboardMatare DateHeader="@panel.DateHeader" Header="@panel.Header" ></DashboardMatare>
            </CascadingValue>
        </Column>
    }
</Row>

It fetches data via OnParametersSetAsync and its working fine, it fetches new panels and renders them

But when I swap to another dashboard the same values for the previous dashboard is still there for "DashboardMatareChartJs".

It will render new panels if the amount of panels differs between the dashboards.

I cascade the panelId to the children so blazor will understand that it needs to change state.

DashboardMatare:

<DashboardContainer>

<DashboardHeader Header="@Header" DateHeader="@DateHeader"></DashboardHeader>

<CascadingValue Value=PanelId>
    <DashboardMatareChartJs />
</CascadingValue>

DashboardMatareChartJs:

if (Data == null)
{
    <Loader />
    return;
}

<div class="pl-3">
    <Row NoGutters="true" Class="h-75">
        <Column Class="col-12">
            <div class="matare">
                <div class="valueWrapper">
                    <canvas class="meterGraph" ref="ReferenceToChartJs">
                    </canvas>
                </div>
            </div>
        </Column>
    </Row>
    <Row>
        <Column Class="text-left col-12">
            <DashboardJamforMed JamforMedText="Data.JamforMedText" IsProcent="Data.ShowPercentage"></DashboardJamforMed>
        </Column>
    </Row>
</div>


[Inject]
protected HttpClient HttpClient { get; set; }

[CascadingParameter]
public Guid PanelId { get; set; }

public ElementReference ReferenceToChartJs { get; set; }

[Inject]
private IJSRuntime JsRuntime { get; set; }

public DashboardPanelGraphDataDto Data { get; set; }

protected override async Task OnInitializedAsync()
{
    Data = await HttpClient.GetFromJsonAsync<DashboardPanelGraphDataDto>($"api/DashboardBlazor/GetChartData/{PanelId}");
}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (Data == null || ReferenceToChartJs.Context == null)
    {
        return;
    }

    await JsRuntime.InvokeVoidAsync("chartService.drawGraph", ReferenceToChartJs, Data.ChartData, Data.TypeOfGraph, Data.ShowPercentage);
}

The DashboardHeader will always have correct data

The problem seems to be that the component wont call OnInitializedAsync when the PanelId is changed. I think its a structure problem, cause I've tried to force statehaschanged via @ref and call StateHasChanged from parent, still does not work.

If I fetch data via OnParametersSetAsync on "DashboardMatareChartJs" then it will load new data, but it's loading it several times and for panels that are not still in view. Im aware that this means that I probably dont understand the lifecycle of blazor 100% yet, so hopefully someone can push me in the right direction.

Thanks in advance!


Solution

  • Try to use the @key directive attribute to force Blazor to rebuild the DashboardMatare objects, like this:

    <DashboardMatare @key="@panel.Id" DateHeader="@panel.DateHeader" Header="@panel.Header" ></DashboardMatare>
    

    This answer is based on the following:

    You can also use @key to prevent Blazor from preserving an element or component subtree when an object changes:

    <div @key="currentPerson">
        ... content that depends on currentPerson ...
    </div>
    

    If @currentPerson changes, the @key attribute directive forces Blazor to discard the entire and its descendants and rebuild the subtree within the UI with new elements and components. This can be useful if you need to guarantee that no UI state is preserved when @currentPerson changes.

    Link to Source...