MBeckius
MBeckius

Reputation: 1527

Async Fetch of Data when a Property Set in Blazor

What is the proper way to fetch data from an async method when a property value changes? Currently, I am doing something like this.

  <form>
      <select class="form-control" @bind="DayFilter">
         <option>None</option>
         <option>Monday</option>
         <option>Tuesday</option>
         <option>Wednesday</option>
         <option>Thursday</option>
         <option>Friday</option>
         <option>Saturday</option>
         <option>Sunday</option>
       </select>
  </form>

@code
{
    private string dayFilter = NoDayFilter;
    private string DayFilter
    {
        get => dayFilter;
        set
        {
            dayFilter = value;
            _ = GetData();
        }
    }

private async Task GetData()
{
   ....
   StateHasChanged();
}

}

I am guessing this is not correct according to the Blazor Docs:

Asynchronous work when applying parameters and property values must occur during the OnParametersSetAsync lifecycle event.

Upvotes: 1

Views: 3812

Answers (2)

MBeckius
MBeckius

Reputation: 1527

Calling an async method from a property setter is definitely not OK if you want the UI to update. After thinking about the answers that enet and Henk Holterman gave, I added some debug code

    set
    {
        dayFilter = value;
        _ = GetData();
        Debug.WriteLine("Day Filter After GetData()");
    }

    private async Task GetData()
    {
         Debug.WriteLine("Before http");
         var temp = await Http.GetFromJsonAsync<DailyData[]>("https://...");
         Debug.WriteLine("After http");
         ...
     }

Which produced the following output:

  • Before http
  • Day Filter After GetData()
  • After http

Based on that, I think the UI finished updating prior to the data being fetched. Since there is no way to await from a setter, my original solution is not workable without calling StateHasChanged- which I want to avoid as I think it would lead to multiple UI updates.

Upvotes: 0

enet
enet

Reputation: 45626

Your doubts about whether you employ async calls properly are not really related to the component life cycle methods...

What you're doing is fine, and it should work. If you use the async key word your method is asynchronous... But you don't have to call the StateHasChanged method. It is called automatically when UI events are involved and when an async mehtod is used: If you use async method, you must await the called method, as for instance:

private async Task GetData()
{
   forecasts = await httpClient.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
}

When the runtime executes this method, and notices the await key word, it yields control to the calling code... When the GetFromJsonAsync method returns, execution continues, synchronously to the end of the method... Also the StateHasChanged method is automatically called, and your component is re-render.

The only issue I find with your code, is calling GetData() from the set accessor. This works and all, but I would call the GetData() from an event handler set for the selection of a new item ( change event).

UPDATE

"firing and forgetting" is fine. But you must use the await key word in your GetData method, which is why you need to call StateHasChanged manually, without which there is no way for the component to know when the call has completed. I guess if you place await Task.CompletedTask; you're going to solve the issue.

I did consider just using the OnChange event, but I like the aesthetics the property binding approach

'change' event you mean, right?

What aesthetics ? The compiler is going to produce a different code for you, anyway, with a 'value' attribute and 'onchange' event.

I hope that helps! If you get stuck, let me know

Upvotes: 1

Related Questions