aspnetcore/blazor/components/overwriting-parameters.md
By Robert Haken
This article explains how to avoid overwriting parameters in Blazor apps during rerendering.
The Blazor framework generally imposes safe parent-to-child parameter assignment:
A child component receives new parameter values that possibly overwrite existing values when the parent component rerenders. Accidentally overwriting parameter values in a child component often occurs when developing the component with one or more data-bound parameters and the developer writes directly to a parameter in the child:
The potential for overwriting parameter values extends into the child component's property set accessors, too.
[!IMPORTANT] Our general guidance is not to create components that directly write to their own parameters after the component is rendered for the first time.
Consider the following ShowMoreExpander component that:
InitiallyExpanded).After the following ShowMoreExpander component demonstrates an overwritten parameter, a modified ShowMoreExpander component is shown to demonstrate the correct approach for this scenario. The following examples can be placed in a local sample app to experience the behaviors described.
ShowMoreExpander.razor:
:::moniker range=">= aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/BadShowMoreExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/BadShowMoreExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"
:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/BadShowMoreExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0"
:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/BadShowMoreExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0"
:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/BadShowMoreExpander.razor":::
:::moniker-end
:::moniker range="< aspnetcore-5.0"
:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/overwriting-parameters/BadShowMoreExpander.razor":::
:::moniker-end
The ShowMoreExpander component is added to the following Expanders parent component that may call xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A:
@onclick directive attribute attaches an event handler to the button's onclick event. Event handling is covered in more detail later in xref:blazor/components/event-handling.Expanders.razor:
:::moniker range=">= aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/Expanders.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/Expanders.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"
:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/overwriting-parameters/Expanders.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0"
:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/overwriting-parameters/Expanders.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0"
:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/overwriting-parameters/Expanders.razor":::
:::moniker-end
:::moniker range="< aspnetcore-5.0"
:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/overwriting-parameters/Expanders.razor":::
:::moniker-end
Initially, the ShowMoreExpander components behave independently when their InitiallyExpanded properties are set. The child components maintain their states as expected.
If xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A is called in a parent component, the Blazor framework rerenders child components if their parameters might have changed:
For the Expanders component:
ShowMoreExpander component sets child content in a potentially mutable xref:Microsoft.AspNetCore.Components.RenderFragment, so a call to xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A in the parent component automatically rerenders the component and potentially overwrites the value of InitiallyExpanded to its initial value of false.ShowMoreExpander component doesn't set child content. Therefore, a potentially mutable xref:Microsoft.AspNetCore.Components.RenderFragment doesn't exist. A call to xref:Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged%2A in the parent component doesn't automatically rerender the child component, so the component's InitiallyExpanded value isn't overwritten.To maintain state in the preceding scenario, use a private field in the ShowMoreExpander component to maintain its state.
The following revised ShowMoreExpander component:
InitiallyExpanded component parameter value from the parent.expanded) in the OnInitialized event.[!NOTE] The advice in this section extends to similar logic in component parameter
setaccessors, which can result in similar undesirable side effects.
ShowMoreExpander.razor:
:::moniker range=">= aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/ShowMoreExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/ShowMoreExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"
:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/ShowMoreExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0"
:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/ShowMoreExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0"
:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/ShowMoreExpander.razor":::
:::moniker-end
:::moniker range="< aspnetcore-5.0"
:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/overwriting-parameters/ShowMoreExpander.razor":::
:::moniker-end
[!NOTE] The revised
ShowMoreExpanderdoesn't reflect changes to theInitiallyExpandedparameter after initialization (OnInitialized). In certain scenarios, an already initialized component might receive new parameter values. This can happen, for example, in a primary-subordinate view where the same component is used to render different detail views or when the/item/{id}route parameter changes to display a different item.
Consider following ToggleExpander component that:
ToggleExpander.razor:
:::moniker range=">= aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/ToggleExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/ToggleExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"
:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/ToggleExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0"
:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/ToggleExpander.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0"
:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Shared/overwriting-parameters/ToggleExpander.razor":::
:::moniker-end
:::moniker range="< aspnetcore-5.0"
:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Shared/overwriting-parameters/ToggleExpander.razor":::
:::moniker-end
The ToggleExpander component should be used with the @bind-Expanded="{field}" binding syntax, allowing two-way synchronization of the parameter.
ExpandersToggle.razor:
:::moniker range=">= aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/9.0/BlazorSample_BlazorWebApp/Components/Pages/ExpandersToggle.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0"
:::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_BlazorWebApp/Components/Pages/ExpandersToggle.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"
:::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/overwriting-parameters/ExpandersToggle.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0"
:::code language="razor" source="~/../blazor-samples/6.0/BlazorSample_WebAssembly/Pages/overwriting-parameters/ExpandersToggle.razor":::
:::moniker-end
:::moniker range=">= aspnetcore-5.0 < aspnetcore-6.0"
:::code language="razor" source="~/../blazor-samples/5.0/BlazorSample_WebAssembly/Pages/overwriting-parameters/ExpandersToggle.razor":::
:::moniker-end
:::moniker range="< aspnetcore-5.0"
:::code language="razor" source="~/../blazor-samples/3.1/BlazorSample_WebAssembly/Pages/overwriting-parameters/ExpandersToggle.razor":::
:::moniker-end
For more information on parent-child binding, see the following resources:
For more information on change detection, including information on the exact types that Blazor checks, see xref:blazor/components/rendering#rendering-conventions-for-componentbase.