O componente pai do Blazor é reinicializado inesperadamente após o evento onclick no filho
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
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
Você tem um oculto async void
e, 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.
Espero que seu pai esteja dentro de um EditForm
e você esteja alterando seu Model
parâmetro. Isso faz com que todos os componentes dentro dele sejam destruídos e recriados.
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.