Search code examples
c#.netasp.net-corerazorblazor

Blazor Parent Component unexpectedly gets Reinitialized after onclick event in child


So, I have a Parent and Child component in a blazor server app. The parent component is in the index.razor page It takes no params. The child component has a List as a parameter:

...
@if (People == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <li>

    @foreach (var p in People)
    {
        <ul>@p.LastName, @p.FirstName</ul>
    }
    </li>
}

@code {
    [Parameter]
    public IReadOnlyList<PersonModel> People { get; set; }
}
...

In the parent, there's a form with a button that does something and builds a List, then passes this list as a parameter into the child component:

...
<button class="btn btn-outline-primary" type="submit" @onclick="(() => SearchPeople(firstNameInput, lastNameInput))">Search</button>

...
        @if(peopleResults != null)
        {
            <PeopleList People="peopleResults"></PeopleList>
        }

@code {

    private string lastNameInput = string.Empty;
    private string firstNameInput = string.Empty;

    private List<PersonModel> peopleResults {
        get;
        set; }

    private async Task SearchPeople(string lastName, string firstName)
    {
        peopleResults = await _personData.SearchPeopleByName(firstName, lastName);
    }

}

The SearchPeople method works fine and returns the correct data. I've debugged the program and it looks like it does start to build the child component with the List properly. In fact, in some cases I've seen it pop up on the page for a second. However, after that, the parent component gets completely re initialized and all properties are set back to defaults. I've put a

protected async override Task OnInitializedAsync() {
}

method call in the parent component and a breakpoint in there, and sure enough it's getting called some point after the onclick event. So the parent component is definitely getting wiped and reinitialized every time I click the button.

So my question is, why would blazor re initialize the parent component? I followed the advice of this post for this, and I'm not sure what I'm doing differently: How can I pass a List<string> as a parameter to a child component in blazor?

I thought it maybe could be due to the fact that i'm passing a mutable parameter into the child, and if the child mutates it, blazor thinks the parent should be re-initialized, but the param lives in the parent, so that wouldn't make sense.


Solution

  • You have a hidden async void and because of that re-rendering is out of sync.

    Change this line

    <button class="btn btn-outline-primary" type="submit" 
      @onclick="(() => SearchPeople(firstNameInput, lastNameInput))">Search</button>
    

    to:

    <button class="btn btn-outline-primary" type="submit" 
      @onclick="(async () => await SearchPeople(firstNameInput, lastNameInput))">Search</button>
    

    and you probably want type="button" there. Not the main problem.


    When you handle a user event like @onclick Blazor will do an implicit StateHasChanged() after the handler, causing a rerendering of the page.

    SearchPeople() is an async method but in the original event handler it was not awaited. So it operated in fire-and-forget mode and the rerendering happened before the method had completed.