RadzenTabs renders stale data when nested in a RenderFragment<T> #96

Closed
opened 2026-01-29 17:31:27 +00:00 by claunia · 4 comments
Owner

Originally created by @jgreen-zynga on GitHub (May 18, 2021).

Describe the bug
RadzenTabs renders stale data when nested in a RenderFragment that takes a context. Some part of the render system appears to be one reference behind when rendering a "context" value into a tab's content.

To Reproduce
Steps to reproduce the behavior:

  1. Download and Run https://github.com/jgreen-zynga/blazor-tabs/tree/main
  2. Open root page localhost:5001/
  3. Observe the i value rendered as tab content is one behind the actual value (displayed above the tab content)

Expected behavior
I expect the tab content to render the most recent value of the context.

Screenshots
Screen Shot 2021-05-17 at 7 16 53 PM

Desktop (please complete the following information):

  • OS: macOSX
  • Browser chrome
  • Version 90

Additional context
The problem is reproducible with or without styling, so I've left it out for the example project.
Above the RadzenTabs component is a dead-simple component I used to verify it wasn't just nested content that was the problem.
Below is a minimalist reproduction of the issue, mimicking the core render flow of the RadzenTabs.

Originally created by @jgreen-zynga on GitHub (May 18, 2021). **Describe the bug** RadzenTabs renders stale data when nested in a RenderFragment<T> that takes a context. Some part of the render system appears to be one reference behind when rendering a "context" value into a tab's content. **To Reproduce** Steps to reproduce the behavior: 1. Download and Run https://github.com/jgreen-zynga/blazor-tabs/tree/main 2. Open root page `localhost:5001/` 3. Observe the `i` value rendered as tab content is one behind the actual value (displayed above the tab content) **Expected behavior** I expect the tab content to render the most recent value of the context. **Screenshots** <img width="361" alt="Screen Shot 2021-05-17 at 7 16 53 PM" src="https://user-images.githubusercontent.com/6938881/118567828-80b5f680-b744-11eb-8046-a8d5a2f198d6.png"> **Desktop (please complete the following information):** - OS: macOSX - Browser chrome - Version 90 **Additional context** The problem is reproducible with or without styling, so I've left it out for the example project. Above the RadzenTabs component is a dead-simple component I used to verify it wasn't just nested content that was the problem. Below is a minimalist reproduction of the issue, mimicking the core render flow of the RadzenTabs.
Author
Owner

@jgreen-zynga commented on GitHub (May 18, 2021):

Further investigation shows Radzen's approach appears to reflect the examples provided by the Microsoft documentation, I've opened a ticket with them https://github.com/dotnet/aspnetcore/issues/32803

@jgreen-zynga commented on GitHub (May 18, 2021): Further investigation shows Radzen's approach appears to reflect the examples provided by the Microsoft documentation, I've opened a ticket with them https://github.com/dotnet/aspnetcore/issues/32803
Author
Owner

@jeremysalwen commented on GitHub (Nov 20, 2024):

@jgreen-zynga The aspnetcore issue was closed and locked without any response. Could you clarify a bit how you resolved this issue? I am getting the same behavior (RadzenTabs content not updating, until you click somewhere on the tab, despite StateHasChanged(), etc.).

@jeremysalwen commented on GitHub (Nov 20, 2024): @jgreen-zynga The aspnetcore issue was closed and locked without any response. Could you clarify a bit how you resolved this issue? I am getting the same behavior (RadzenTabs content not updating, until you click somewhere on the tab, despite StateHasChanged(), etc.).
Author
Owner

@jeremysalwen commented on GitHub (Nov 21, 2024):

My own investigation of the jgreen-zynga's example repo found a workaround: Calling StateHasChanged multiple times. Specifically changing AsyncWorkComponent.razor as follows:

<div>Async I: @i
@if (ChildContent != null)
{
    @ChildContent(i)
}
</div>
@code {
    int i;

    [Parameter]
    public RenderFragment<int> ChildContent { get; set; }

    protected override async Task OnParametersSetAsync()
    {
        await Task.Delay(TimeSpan.FromSeconds(3));
        i = 1;
        Loop();
        StateHasChanged(); // Extra call to StateHasChanged (effectively rerendering twice)
    }

    async void Loop()
    {
        while (i < 4)
        {
            await Task.Delay(TimeSpan.FromSeconds(3));
            i++;
            Console.WriteLine($"Async Loop {i}");
            StateHasChanged();
            StateHasChanged();  // Second call to StateHasChanged!
        }
    }
    

}

Will make RadzenTabs stop rendering stale data. Forcing everything to render twice seems wasteful (and probably brittle), but at least is a workaround for now.

Also note that for the simpler non-radzen example in that repository, this additional change to ParentComponent.razor was also necessary to get the first update to be rendered (strangely enough, the later updates were rendered properly):

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            StateHasChanged();
        }
    }

Note that the rerendering (StateHasChanged) needs to be done from the parent component. Calling StateHasChanged on the child component won't do anything. If you have e.g. a button inside the tab that needs to force a rerender, you need to somehow call StateHasChanged on the parent component. I created a workaround for my use case CascadingValues because I cannot edit RadzenTabs and RadzenTabsItem directly.

@using Radzen
<CascadingValue Value="this">
    <RadzenTabs RenderMode="RenderMode">
        <Tabs>
            @ChildContent
        </Tabs>
    </RadzenTabs>
</CascadingValue>

@code {

    [Parameter]
    public RenderFragment ChildContent { get; set; } = null!;

    [Parameter]
    public TabRenderMode RenderMode { get; set; }

    public void TriggerStateHasChanged()
    {
        StateHasChanged();
    }
}

I believe that this could be implemented more simply directly inside RadzenTabs, where StateHasChanged gets called on the RadzenTabs parent when the child RadzenTabsItem handles an event. @akorchev could you reopen this issue, since it is still relevant to the latest versions of Radzen?

@jeremysalwen commented on GitHub (Nov 21, 2024): My own investigation of the jgreen-zynga's [example repo](https://github.com/jgreen-zynga/blazor-tabs/tree/example-tabs) found a workaround: Calling `StateHasChanged` multiple times. Specifically changing `AsyncWorkComponent.razor` as follows: ``` <div>Async I: @i @if (ChildContent != null) { @ChildContent(i) } </div> @code { int i; [Parameter] public RenderFragment<int> ChildContent { get; set; } protected override async Task OnParametersSetAsync() { await Task.Delay(TimeSpan.FromSeconds(3)); i = 1; Loop(); StateHasChanged(); // Extra call to StateHasChanged (effectively rerendering twice) } async void Loop() { while (i < 4) { await Task.Delay(TimeSpan.FromSeconds(3)); i++; Console.WriteLine($"Async Loop {i}"); StateHasChanged(); StateHasChanged(); // Second call to StateHasChanged! } } } ``` Will make RadzenTabs stop rendering stale data. Forcing everything to render twice seems wasteful (and probably brittle), but at least is a workaround for now. Also note that for the simpler non-radzen example in that repository, this additional change to `ParentComponent.razor` was also necessary to get the *first* update to be rendered (strangely enough, the later updates were rendered properly): ``` protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { StateHasChanged(); } } ``` Note that the rerendering (`StateHasChanged`) needs to be done from the parent component. Calling `StateHasChanged` on the child component won't do anything. If you have e.g. a button *inside* the tab that needs to force a rerender, you need to somehow call `StateHasChanged` on the parent component. I created a workaround for my use case `CascadingValues` because I cannot edit `RadzenTabs` and `RadzenTabsItem` directly. ``` @using Radzen <CascadingValue Value="this"> <RadzenTabs RenderMode="RenderMode"> <Tabs> @ChildContent </Tabs> </RadzenTabs> </CascadingValue> @code { [Parameter] public RenderFragment ChildContent { get; set; } = null!; [Parameter] public TabRenderMode RenderMode { get; set; } public void TriggerStateHasChanged() { StateHasChanged(); } } ``` I believe that this could be implemented more simply directly inside `RadzenTabs`, where `StateHasChanged` gets called on the `RadzenTabs` parent when the child `RadzenTabsItem` handles an event. @akorchev could you reopen this issue, since it is still relevant to the latest versions of Radzen?
Author
Owner

@jgreen-zynga commented on GitHub (Mar 3, 2025):

Could you clarify a bit how you resolved this issue?

@jeremysalwen apologies, just saw this. I forced the page to render twice per update. The page was fairly simple so it solved my immediate issue, but to the best of my knowledge it's still present.

@jgreen-zynga commented on GitHub (Mar 3, 2025): > Could you clarify a bit how you resolved this issue? @jeremysalwen apologies, just saw this. I forced the page to render twice per update. The page was fairly simple so it solved my immediate issue, but to the best of my knowledge it's still present.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/radzen-blazor#96