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.78k stars 363 forks source link

fix: Dispatcher Exception in Debouncer 4.8.x+ #2466

Closed Mike-E-angelo closed 2 months ago

Mike-E-angelo commented 2 months ago

🐛 Bug Report

Setting the Immediate property to true on the FluentTextField in any version after 4.7 results in a dispatcher exception as it appears the invoked delegate is not being invoked on the same dispatcher of the component.

💻 Repro or Code Sample

I am encountering this on the FluentTextField when it sets the Immediate property to true when the version of Microsoft.FluentUI.AspNetCore.Components is anything 4.8.x or above. This problem does not happen on 4.7.2.

🤔 Expected Behavior

No errors, please. 😊 The debouncer should execute the delegate on the same thread as the component dispatcher.

😯 Current Behavior

In the case of FluentTextField with Immediate=true, I get the error here in 4.8.x+:

image

💁 Possible Solution

I am currently reverting back to 4.7.x to avoid this issue.

🔦 Context

Everything else works well. 😊

🌍 Your Environment

dvoituron commented 2 months ago

I cannot reproduce your issue.

I've added Immediate to the first sample of the demo site (server project), and all seems correct.

<p>Without label:</p>
<FluentTextField @bind-Value=value1 Immediate AriaLabel="No label"></FluentTextField>
<p>You entered: @value1</p>

Can you give us a sample code to reproduce this problem?

Mike-E-angelo commented 2 months ago

Hi @dvoituron thank you for your investigation and patience. Immediate="true" is one part, but I just now learned that you need to set the ImmediateDelay to introduce this issue as that is what incorporates the Debouncer:

@page "/"
@rendermode InteractiveServer

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<EditForm Model="@Subject">
    <DataAnnotationsValidator />
    <Microsoft.FluentUI.AspNetCore.Components.FluentTextField @bind-Value="@Subject.Message" Immediate="true" ImmediateDelay="750" />
</EditForm>
@code
{
    [Parameter]
    public Model Subject { get; set; } = new();

    public sealed class Model
    {
        public string Message { get; set; } = "hello world";
    }
}
Mike-E-angelo commented 2 months ago

FWIW I am using these classes as a workaround for now.

cduluCNB commented 2 months ago

I also want to report that I'm also getting the same message with the same stack trace (please see attachments), except with the FluentSearch component.

The SearchTextBoxChanged is an async method, that gets the data and populates a FluentDataGrid. This is the following pseudocode:

MyGrid?.SetLoadingState(true); await InvokeAsync(StateHasChanged);

MyList = await GetListAsync();

MyGrid?.SetLoadingState(false); await InvokeAsync(StateHasChanged);

Not sure if I need to be invoking StateHasChanged after setting the loading state on the grid, however.

FluentSearch 20240806 Call Stack 20240806 Error 20240806

vnbaaij commented 2 months ago

I also want to report that I'm also getting the same message with the same stack trace (please see attachments), except with the FluentSearch component.

Please create a new issue (with reproduction steps/code!!) for this.

vnbaaij commented 2 months ago

Fixed this by changing OnValidateStateChanged in FluentInputBase to use InvokeAsync(StateHasChanged); instead of just StateHasChanged().

Will be in next release.

cduluCNB commented 2 months ago

Hi @vnbaaij thank you so much!

Since you made the fix in FluentInputBase, do I still need to create a new issue for FluentSearch?

vnbaaij commented 2 months ago

Probably not, no. Please try the preview package and see if it resolves your issue too. To use the preview NuGet Feed see https://github.com/microsoft/fluentui-blazor/blob/dev/docs/using-latest-daily.md

cduluCNB commented 1 month ago

Hi @vnbaaij I'm still getting an error, I was able to narrow it down to the following line in FluentInputBase.cs:

I've committed a fix that I'll create a pull request for, but before I do that, I need to do some more investigation as to why my app is throwing this specific error.

2024-09-04 13_11_33 - FluentInputBase cs

cduluCNB commented 1 month ago

I did a bit more investigation and the issue happens with the following conditions:

Not sure how to describe this, but it seems like there's an issue (race condition?) during validation, when the EditContext is moved to a separate thread from the Dispatcher because of the Debouncer.

I'll be submitting the pull request - it's a small change, basically calling InvokeAsync to bring back the EditContext into the same thread as the dispatcher.

Here is some repro code:

@page "/"
@using System.ComponentModel.DataAnnotations

<EditForm EditContext="@_formContext">
    <DataAnnotationsValidator />
    <FluentValidationSummary/>
    @* <ValidationSummary /> *@
    <FluentTextField @bind-Value="@_search!.SearchText"
                     Immediate="true"
                     ImmediateDelay="500" />
    <FluentValidationMessage For="() => _search!.SearchText" />
</EditForm>

@code
{
    private SearchModel? _search;
    private EditContext? _formContext;

    protected override void OnInitialized()
    {
        base.OnInitialized();

        _search = new SearchModel() { SearchText = string.Empty };
        _formContext ??= new EditContext(_search);
    }

    public class SearchModel()
    {
        [StringLength(4, ErrorMessage = "Search term is too long.")]
        public string? SearchText { get; set; }
    }
}