Estoy aprendiendo Blazor leyendo la documentación de Blazor de Microsoft y tengo problemas para entender este ejemplo sobre el problema de parámetros sobrescritos . Aquí está el código de ejemplo:
Componente hijo:
<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem"> <div class="card-body"> <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2> @if (Expanded) { <p class="card-text">@ChildContent</p> } </div> </div> @code { [Parameter] public bool Expanded { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } private void Toggle() { Expanded = !Expanded; } }Padre:
@page "/expander-example" <Expander Expanded="true"> Expander 1 content </Expander> <Expander Expanded="true" /> <button @onclick="StateHasChanged"> Call StateHasChanged </button>Lo que me confunde es:
¿Por qué no se vuelve a renderizar el segundo Expander y tampoco funciona el enlace de parámetros?
¿Cuál es exactamente el problema que la documentación intenta mostrar aquí? Cuando un componente principal restablece su estado, espero que el cambio se refleje en los componentes secundarios, que es exactamente lo que hace el primer expansor supuestamente problemático.
- ¿Por qué no se vuelve a renderizar el segundo Expansor y tampoco funciona el enlace de parámetros?
Porque hay un parámetro de un tipo de valor simple y un tipo de referencia (ChildContent) que es nulo. El motor de renderizado decide "nada ha cambiado aquí" y lo omite.
Que ese parámetro booleano haya sido cambiado 'desde adentro' es precisamente el problema de los 'parámetros sobrescritos'.
El primer <Expander> tiene un ChildContent no nulo y luego el motor no mira más profundo, asume que ese es un objeto mutable y vuelve a representar el componente.
Tenga en cuenta que eso significa que hay un costo oculto al pasar objetos complejos en lugar de tipos de valor. <PersonCard PersonId="person.Id" /> no se volverá a procesar con tanta frecuencia como <PersonCard Person="person" />
El problema se describe en este párrafo.
Un componente secundario recibe nuevos valores de parámetros que posiblemente sobrescriben los valores existentes cuando el componente principal se vuelve a representar. La sobrescritura accidental de valores de parámetros en un componente secundario suele ocurrir cuando se desarrolla el componente con uno o más parámetros enlazados a datos y el desarrollador escribe directamente en un parámetro en el componente secundario.
Si no desea que el componente secundario dependa de la propiedad principal, no pase esta propiedad como parámetro al elemento secundario. Para que el ejemplo sea explícito, activan el método StateHasChanged para iniciar la detección de cambios en la página principal. Esto, a su vez, desencadena la reproducción del elemento secundario, lo que puede no ser un comportamiento deseado si desea mantener abierto el expansor secundario. Es por eso que recomiendan usar una variable privada dentro del niño para aislar su estado del padre.
Sin embargo, este NO es el mayor problema :)
Lo que MSDN no le dice es que al trabajar con Blazor, tarde o temprano se dará cuenta de que su política de detección de cambios es significativamente diferente en comparación con los marcos SPA ampliamente utilizados, como Angular o React. Estos marcos detectan solo cambios del estado interno (variables de clase) definidos en el componente. Por el contrario, a Blazor le gusta volver a renderizar la página completa y todos sus elementos secundarios, incluso en algunos casos arbitrarios, por ejemplo, cuando hizo clic en algún lugar aleatorio de la página.
¿Cómo detengo que blazor @onclick vuelva a renderizar la página?
En otras palabras, es posible que vea el mismo problema incluso si no llama a StateHasChanged explícitamente.
¿Cuál es exactamente el problema que la documentación intenta mostrar aquí? Cuando un componente principal restablece su estado, espero que el cambio se refleje en los componentes secundarios, que es exactamente lo que hace el primer expansor supuestamente problemático.
El objetivo es advertir contra el uso de parámetros públicos para mantener el estado interno de un componente que desea controlar solo desde el interior del componente.
Visualice un escenario en el que tenga un Título editable y sus detalles en el estado contraído. La clase modelo Título también tiene algunas otras propiedades (Omitir por brevedad).
Considera lo siguiente:
<TitleEditor Title="@title" @OnTitleChanged="RefreshTitle"/> <Details Content="@content" CollapseState="@areDetailsCollapsed"/> @code { TitleClass title = new (); ContentClass content = new (); bool areDetailsCollapsed = true; void RefreshTitle(string title) { title.Text = title; StateHasChanged(); } } El usuario actualiza el título. Esto llama a "RefreshTitle" que está destinado a actualizar solo el título. Sin embargo, dado que los parámetros del tipo de referencia están involucrados, los Details también se actualizan. Eso dará como resultado que se restablezca el estado de colapso interno de Details . Lo cual es un efecto secundario totalmente no deseado.
Además, la cascada StateHasChanged() se actualiza en cada componente secundario del árbol. Por lo tanto, cualquier StateHasChanged() en cualquier lugar de cualquier nodo principal hará que un componente pierda su estado.