O componente pai do Blazor é reinicializado inesperadamente após o evento onclick no filho

Aug 15 2020

Portanto, eu tenho um componente Pai e Filho em um aplicativo de servidor Blazor. O componente pai está na página index.razor. Não leva parâmetros. O componente filho tem uma lista como parâmetro:

...
@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; }
}
...

No pai, há um formulário com um botão que faz algo e cria uma lista e, em seguida, passa essa lista como um parâmetro para o componente filho:

...
<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);
    }

}

O método SearchPeople funciona bem e retorna os dados corretos. Eu depurei o programa e parece que ele começou a construir o componente filho com a Lista corretamente. Na verdade, em alguns casos, eu o vi aparecer na página por um segundo. No entanto, depois disso, o componente pai é completamente reinicializado e todas as propriedades voltam aos padrões. Eu coloquei um

protected async override Task OnInitializedAsync() {
}

chamada de método no componente pai e um ponto de interrupção nele, e com certeza está sendo chamado algum ponto após o evento onclick. Portanto, o componente pai está definitivamente sendo apagado e reinicializado toda vez que clico no botão.

Portanto, minha pergunta é: por que o blazor reinicializaria o componente pai? Segui o conselho deste post para isso, e não tenho certeza do que estou fazendo de diferente: Como posso passar uma List como um parâmetro para um componente filho no blazor?

Achei que talvez pudesse ser devido ao fato de que estou passando um parâmetro mutável para o filho e, se o filho mudar, blazor acha que o pai deveria ser reinicializado, mas o param mora no pai, então isso não não faz sentido.

Respostas

HenkHolterman Aug 15 2020 at 21:57

Você tem um oculto async voide, por causa disso, a re-renderização está fora de sincronia.

Mude esta linha

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

para:

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

e você provavelmente quer type="button"lá. Não é o problema principal.


Quando você manipula um evento de usuário como @onclick, o Blazor faz um StateHasChanged () implícito após o manipulador, causando uma nova renderização da página.

SearchPeople () é um método assíncrono, mas no manipulador de eventos original ele não era esperado. Portanto, ele operou no modo disparar e esquecer e a renderização ocorreu antes que o método fosse concluído.

PeterMorris Aug 15 2020 at 23:25

Espero que seu pai esteja dentro de um EditForme você esteja alterando seu Modelparâmetro. Isso faz com que todos os componentes dentro dele sejam destruídos e recriados.

JamesHoux Dec 19 2020 at 02:29

Eu tinha um botão em um formulário com um evento OnClick que chamava um método vazio - sim, não fazia nada. Clicar no botão fez com que a página inteira fosse reinicializada junto com todos os seus componentes, e nada do que eu tentei interromperia esse comportamento. A solução era simplesmente se livrar da tag do formulário.

Esta é uma advertência estranha sobre como as formas são tratadas no Blazor, aparentemente.

Sugestão: não use Formulários, a menos que sua implementação definitivamente o chame.