dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.43k stars 10.02k forks source link

Blazor is recycling component instead create new one in a loop with "Lazy" rendering #20563

Closed alexfdezsauco closed 4 years ago

alexfdezsauco commented 4 years ago

Describe the bug

I have a particular an implementation of a Table and Tab components. https://github.com/WildGums/Blorc.PatternFly. That works well so far but when I mixed to work together something strange happened. I have a kind of lazy rendering approach, to actually render the content of the tab. But in the specific case of the Table usage, Blazor started to recycling component instead create new one in a loop. In the example I have two 3 tables and 2 with the same schema.

As workround I have include conditions in order to create new instances components. The expected behavior is that Blazor create a new instance of the table per tab.

To Reproduce

Here is the source demo.

https://github.com/WildGums/Blorc.PatternFly/blob/99799bda730b344a26d80221f4a715ebbfa64e9d/src/Blorc.PatternFly.Example/Pages/Components/TabsDemo.razor#L203

in a real live problem I have to do this to create new tables.

  <Tabs IsFilled="true" @bind-SelectedId="@SelectedId" @bind-SelectedId:event="SelectedTabChanged">
        @for (int idx = 0; idx < this.Names.Count; idx++)
        {
            <!--TODO: Fix this as soon as possible, this looks like Blazor bug-->
            <Tab Id=@idx Title="@Names[idx]">
                @if (SelectedId == 0)
                {
                    <Table Id="@GetId()" DataSource="@Data[SelectedId]" Columns="@Schema[SelectedId]" />
                }
                @if (SelectedId == 1)
                {
                    <Table Id="@GetId()" DataSource="@Data[SelectedId]" Columns="@Schema[SelectedId]" />
                }
                @if (SelectedId == 2)
                {
                    <Table Id="@GetId()" DataSource="@Data[SelectedId]" Columns="@Schema[SelectedId]" />
                }
                @if (SelectedId == 3)
                {
                    <Table Id="@GetId()" DataSource="@Data[SelectedId]" Columns="@Schema[SelectedId]" />
                }
                ...

-->

Further technical details

Runtime Environment: OS Name: Windows OS Version: 10.0.18363 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\3.1.103\

Host (useful for support): Version: 3.1.3 Commit: 4a9f85e9f8

.NET Core SDKs installed: 3.0.101 [C:\Program Files\dotnet\sdk] 3.1.100 [C:\Program Files\dotnet\sdk] 3.1.103 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed: Microsoft.AspNetCore.All 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

javiercn commented 4 years ago

@alexfdezsauco thanks for contacting us.

It is not a bug, "recycling" components is the default behavior by design to avoid excessive garbage collection. In your use case, I think you are looking for something like key to make it work.

Blazor will diff the two doms and will always produce the minimal set of changes that need to be done on the page to achieve the new state. If you don't reset the state of your components when SetParametersAsync is called or if you modify the elements with JavaScript and so on, you will get spurious results on the UI.

alexfdezsauco commented 4 years ago

Sorry, my mistake. I noticed the behavior and tried with component "id", but exactly @key is the solution. Great!