diff --git a/aspnetcore/blazor/components.md b/aspnetcore/blazor/components.md index c2ff1f2e731f..3d90b2b4c743 100644 --- a/aspnetcore/blazor/components.md +++ b/aspnetcore/blazor/components.md @@ -5,13 +5,13 @@ description: Learn how to create and use Razor components, including how to bind monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 03/25/2020 +ms.date: 04/21/2020 no-loc: [Blazor, SignalR] uid: blazor/components --- # Create and use ASP.NET Core Razor components -By [Luke Latham](https://github.com/guardrex) and [Daniel Roth](https://github.com/danroth27) +By [Luke Latham](https://github.com/guardrex), [Daniel Roth](https://github.com/danroth27), and [Tobias Bartsch](https://www.aveo-solutions.com/) [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/blazor/common/samples/) ([how to download](xref:index#how-to-download-a-sample)) @@ -134,6 +134,9 @@ In the following example from the sample app, the `ParentComponent` sets the val [!code-razor[](components/samples_snapshot/ParentComponent.razor?highlight=5-6)] +> [!WARNING] +> Don't create components that write to their own *component parameters*, use a private field instead. For more information, see the [Don't create components that write to their own parameter properties](#dont-create-components-that-write-to-their-own-parameter-properties) section. + ## Child content Components can set the content of another component. The assigning component provides the content between the tags that specify the receiving component. @@ -392,7 +395,7 @@ Consider the following example: The contents of the `People` collection may change with inserted, deleted, or re-ordered entries. When the component rerenders, the `` component may change to receive different `Details` parameter values. This may cause more complex rerendering than expected. In some cases, rerendering can lead to visible behavior differences, such as lost element focus. -The mapping process can be controlled with the `@key` directive attribute. `@key` causes the diffing algorithm to guarantee preservation of elements or components based on the key's value: +The mapping process can be controlled with the [`@key`](xref:mvc/views/razor#key) directive attribute. `@key` causes the diffing algorithm to guarantee preservation of elements or components based on the key's value: ```csharp @foreach (var person in People) @@ -446,6 +449,99 @@ Generally, it makes sense to supply one of the following kinds of value for `@ke Ensure that values used for `@key` don't clash. If clashing values are detected within the same parent element, Blazor throws an exception because it can't deterministically map old elements or components to new elements or components. Only use distinct values, such as object instances or primary key values. +## Don't create components that write to their own parameter properties + +Parameters are overwritten under the following conditions: + +* A child component's content is rendered with a `RenderFragment`. +* is called in the parent component. + +Parameters are reset because the parent component rerenders when is called and new parameter values are supplied to the child component. + +Consider the following `Expander` component that: + +* Renders child content. +* Toggles showing child content with a component parameter. + +```razor +
+ Toggle (Expanded = @Expanded) + + @if (Expanded) + { + @ChildContent + } +
+ +@code { + [Parameter] + public bool Expanded { get; set; } + + [Parameter] + public RenderFragment ChildContent { get; set; } + + private void Toggle() + { + Expanded = !Expanded; + } +} +``` + +The `Expander` component is added to a parent component that may call `StateHasChanged`: + +```razor + +

Hello, world!

+
+ + + + +``` + +Initially, the `Expander` components behave independently when their `Expanded` properties are toggled. The child components maintain their states as expected. When `StateHasChanged` is called in the parent, the `Expanded` parameter of the first child component is reset back to its initial value (`true`). The second `Expander` component's `Expanded` value isn't reset because no child content is rendered in the second component. + +To maintain state in the preceding scenario, use a *private field* in the `Expander` component to maintain its toggled state. + +The following `Expander` component: + +* Accepts the `Expanded` component parameter value from the parent. +* Assigns the component parameter value to a *private field* (`_expanded`) in the [OnInitialized event](xref:blazor/lifecycle#component-initialization-methods). +* Uses the private field to maintain its internal toggle state. + +```razor +
+ Toggle (Expanded = @_expanded) + + @if (_expanded) + { + @ChildContent + } +
+ +@code { + [Parameter] + public bool Expanded { get; set; } + + [Parameter] + public RenderFragment ChildContent { get; set; } + + private bool _expanded; + + protected override void OnInitialized() + { + _expanded = Expanded; + } + + private void Toggle() + { + _expanded = !_expanded; + } +} +``` + ## Partial class support Razor components are generated as partial classes. Razor components are authored using either of the following approaches: diff --git a/aspnetcore/blazor/lifecycle.md b/aspnetcore/blazor/lifecycle.md index 97bd91039797..ec1f340a04a9 100644 --- a/aspnetcore/blazor/lifecycle.md +++ b/aspnetcore/blazor/lifecycle.md @@ -5,7 +5,7 @@ description: Learn how to use Razor component lifecycle methods in ASP.NET Core monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 03/17/2020 +ms.date: 04/16/2020 no-loc: [Blazor, SignalR] uid: blazor/lifecycle --- @@ -205,7 +205,7 @@ For information on handling errors during lifecycle method execution, see