1

In the OnParametersSetAsync method we get the parameters. Based on a parameter (like Id), I need to fetch some async data. I want this initial fetch only once when the component first renders. In order to do that I am managing an extra private boolean field (like _firstRender). So based on this private field I am calling that async method. I want to know is there any other way to achieve this? One more thing is that when StateHasChanged method is necessary to invoke manually and when this gets invoked automatically?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Robin Khan
  • 127
  • 1
  • 10

2 Answers2

2

Taking the Id parameter example, here's a one-page demo showing one way to do it that is similar to yours, but makes the decision based on the Id parameter state.

There's render control included to demonstrate how to render/not render based on state. Note that the conponent always renders first time regardless of the value returned by ShowRender.

@page "/WeatherView/{Id:int}"
@inject NavigationManager NavManager

<h3>WeatherViewer</h3>

<div class="row mb-2">
    <div class="col-3">
        Date
    </div>
    <div class="col-3">
        @this.record.Date.ToLongDateString()
    </div>
</div>
<div class="row mb-2">
    <div class="col-3">
        Temperature &deg;C
    </div>
    <div class="col-3">
        @this.record.TemperatureC
    </div>
</div>
<div class="row mb-2">
    <div class="col-3">
        Summary
    </div>
    <div class="col-6">
        @this.record.Summary
    </div>
</div>
<div class="m-2">
    <button class="btn btn-dark" @onclick="() => this.Move(-1)">Previous</button> 
    <button class="btn btn-primary" @onclick="() => this.Move(1)">Next</button>
</div>

@code {
    private int _id;
    private bool _shouldRender = true;
    private WeatherForecast record = new();

    [Parameter] public int Id { get; set; } = 0;

    protected async override Task OnParametersSetAsync()
    {
        _shouldRender = false;
        var recordChanged = !this.Id.Equals(_id);

        if (recordChanged)
        {
            _id = this.Id;
            this.record = await GetForecast(this.Id);
        }

        _shouldRender = recordChanged;
    }

    protected override bool ShouldRender()
        => _shouldRender;

    private static async ValueTask<WeatherForecast> GetForecast(int id)
    {
        await Task.Delay(100);
        return new WeatherForecast
            {
                Date = DateTime.Now.AddDays(id),
                TemperatureC = id,
                Summary = "Testing"
            };
    }

    private void Move(int value)
        => this.NavManager.NavigateTo($"/WeatherView/{_id + value}");
}

On StateHasChanged, you should only need to call it in certain specific circumstances if you're using ComponentBase:

  1. In event handlers where you change the component state and need to re-render.
  2. In multi-step UI event handlers where you want more than one render when the first await yields to update progress to the user.

Otherwise, you probably have a design issue that you need to address.

MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31
0

At last I realized and found a better way to fetch Asynchronous data from server. I learned that Life cycle methods could be invoked twice or more. Here you can learn more Why are Blazor lifecycle methods getting executed twice? But blazor has OnAfterRenderAsync(bool firstRender) which comes with built in boolean variable that I was trying to manage manually. Here is my code to get initial data from server only once:

protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await GetDataFromServer();
        }
    }
Robin Khan
  • 127
  • 1
  • 10