2
votes

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.

3

3 Answers

1
votes

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.

1
votes

I expect your parent is inside an EditForm and you are changing its Model parameter. That causes all components inside it to be destroyed and recreated.

0
votes

I had a button in a form with an OnClick event that called an empty method -- yes it did nothing at all. Clicking the button caused the entire page to re-initialize along with all of its components, and nothing I tried would stop this behavior. The solution was to simply get rid of the form tag.

This is a weird caveat with how forms are handled in Blazor apparently.

Suggestion: Don't use Forms at all unless your implementation definitely calls for it.