Written by 6:12 pm Blog, Programming

Blazor: OnInitializedAsync Null Exceptions

One of the first issues I encountered when building data-driven Blazor pages is the dreaded NullReferenceException when trying to reference a page component from within the OnInitializedAsync override.

Given the following Razor page source, which follows the recommended pattern of displaying a loading indicator while any asynchronous operations are performed:

@page "/mypage";

@inject IDataRepository DataRepository

@if (this.data == null)
{
    <h1>Loading...</h1>
}
else
{
  <MyComponent @ref="myComponent" />
}

@code {
  private MyComponent myComponent;
  private MyData data;

  protected override async Task OnInitializedAsync()
  {
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
    this.data = await this.DataRepository.GetAsync(cts.Token);

    this.myComponent.Property = "some value";
  }
}

Any attempt to reference components during the page initialization – such as that indicated on line 23 above – will yield a null exception. Given the very simple example above, you may be able to spot the problem, but in case it isn’t obvious, let’s look at why this happens and how we can work around it.

Page Rendering Pipeline

When the above page is first processed for prerendering, it’s very likely that the asynchronous operations inside OnInitializedAsync won’t have completed. This means our data property remains unassigned, causing the conditional test @if (this.data == null) to evaluate as true and as a result, the <MyComponent /> element is not considered for output. Because of this, its @ref="myComponent" attribute is ignored and the property assignment does not happen.

OnAfterRender / OnAfterRenderAsync

So, given the issue above, how and when should we expect to first access our component? Where can we do our one-time setup before the user begins interacting with the page? The answer to both these questions lies with OnAfterRender or its asynchronous counterpart OnAfterRenderAsync. Both of these overrides take a boolean argument firstRender which, as you probably guessed, is true only the first time the page has finished rendering. As per the documentation, this the appropriate time to perform one-time work on the page.

Solution Example

To fully illustrate the fix, here is the updated code snippet with the component logic moved to OnAfterRender (as it isn’t async):

@page "/mypage";

@inject IDataRepository DataRepository

@if (this.data == null)
{
    &lt;h1>Loading...&lt;/h1>
}
else
{
  &lt;MyComponent @ref="myComponent" />
}

@code {
  private MyComponent myComponent;
  private MyData data;

  protected override async Task OnInitializedAsync()
  {
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
    this.data = await this.DataRepository.GetAsync(cts.Token);
  }

  protected override OnAfterRender(bool firstRender)
  {
    if (firstRender)
    {
      this.myComponent.Property = "some value";
    }
  }
}

Now, our page will load correctly and the NullReferenceException has gone.

(Visited 226 times, 1 visits today)
Tags: , , Last modified: 15/02/2020
Close
shares