microsoft / fluentui-blazor

Microsoft Fluent UI Blazor components library. For use with ASP.NET Core Blazor applications
https://www.fluentui-blazor.net
MIT License
3.83k stars 372 forks source link

fix: uncontrolled UI updates of values bound to FluentUI components when using blazorserver and callbacks with some workload #152

Closed Brimerland closed 1 year ago

Brimerland commented 2 years ago

πŸ› Bug Report

Updating values which are two way bound to a FluentUI component during a "@onclick" callback in a Blazor server app will lead to uncontrolled repeated updates if the callback method did not return before a subsequent call.

This issue does not appear on client side Blazor (blazorwasm)!

πŸ’» Repro or Code Sample

The Issue was found using @fluentui/web-components@2.2.3 and Microsoft.Fast.Components.FluentUI 1.1.0.

Use your existing Blazor server app or create a new one following these steps:

Contents of MyItem.razor

@using Microsoft.Fast.Components.FluentUI;

@if (FromInside)
{
    // "normal" use

    <button @onclick="UpdateAsync">Update Async</button>
    <button @onclick="UpdateBlocking">Update Blocking</button>
    <button @onclick="UpdateBlockingAsync">Update Blocking Async</button>
    <button @onclick="UpdateBlockingAsync_Workaround">Update Blocking Async Workaround</button>
    <br>
    @if (UseNumberField)
    {
        <FluentNumberField @bind-Value=BoundValue/>
        // you can also use a slider here
        //<FluentSlider @bind-Value=BoundValue/>
    }
    else
    {
        <input @bind-value=BoundValue />
    }

    @BoundValue    
}
else
{
    // setup to instance to show test

    <p>Press rapidly on the buttons. You will see uncontrolled updates using the 2nd and 3rd buttons in the version using the @nameof(FluentNumberField<int>) </p>
    <p>Binding the value to an input element (working):</p>
    <MyItem FromInside=true UseNumberField=false />
    <br />
    <p>Binding the value to an @nameof(FluentNumberField<int>) element (broken):</p>
    <MyItem FromInside=true UseNumberField=true />    
}

@code
{
    [Parameter]
    public bool FromInside { get; set; }

    [Parameter]
    public bool UseNumberField { get; set; }

    public int BoundValue;

    static int c;

    // ok
    public async Task UpdateAsync()
    {
        await Task.Delay(500);
        BoundValue = ++c;        
    }

    // broken
    public void UpdateBlocking()
    {
        // do some work which might block
        int i;        
        i = 100000000; 
        while (i-- > 0) ;

        BoundValue = ++c;
    }

    // still broken
    public async Task UpdateBlockingAsync()
    {
        await Task.Delay(10);

        // do some blocking work inside a task
        UpdateBlocking();

        await Task.Delay(10);
    }

    // ok
    public Task UpdateBlockingAsync_Workaround()
    {
        return Task.Run(UpdateBlocking);
    }
}

πŸ€” Expected Behavior

Values which are bound to FluentUI components should only update when changed by user or by component code.

😯 Current Behavior

https://user-images.githubusercontent.com/609797/154707861-a55c27d0-ac82-4fc2-a431-11cce1091c7a.mp4

If you press the buttons "Update Blocking" or "Update Blocking Async" in the second row rapidly the bound values keep updating forever. (Even after you have stopped pressing the buttons.)

For comparison a value is bound to a non FluentUI input in the first row. Pressing these buttons do not trigger the issue.

πŸ’ Possible Solution

-

πŸ”¦ Context

If a user triggers this kind of issue the FluentUI components having bound values let the application run wild and not usable anymore. For example a slider or a number field which controls pagination.

🌍 Your Environment

vnbaaij commented 2 years ago

I can reproduce the error. No idea yet what is causing this.

Is there any specific reason you are installing the web-components package? It is not needed for the Microsoft.Fast.Components.FluentUI package to do its thing...

Brimerland commented 2 years ago

Is there any specific reason you are installing the web-components package?

I just installed the package because I needed the web-components version number for this bug report for better reproducibility.

Your "Getting Started" section mentions to use this script element <script type="module" src="https://cdn.jsdelivr.net/npm/@fluentui/web-components/dist/web-components.min.js"></script> to get the latest version. (Which i was using.) But I have no clue to find out which version this 'latest' version is. So, I installed the npm module to get the web-components.min.js and the matching version number.

vnbaaij commented 2 years ago

I just installed the package because I needed the web-components version number for this bug report for better reproducibility.

That's good! Hadn't thought about that.

vnbaaij commented 1 year ago

Thid is no longer happening in the latest version. Closing this