Skip to content

fix: Incorrect usage of InvokeAsync(StateHasChanged) throughout code base. #506

@mrpmorris

Description

@mrpmorris

InvokeAsync(StateHasChanged); is a Blazor anti-pattern.

Blazor ensures that only a single thread is working with a component's state at any given time. If you alter your component through something like a Timer then it bypassess that safety process and calls the code directly in a thread that might access your component state at the same time as another.

To help you to avoid having multiple threads using your component's state at the same time, Blazor will throw an exception when StateHasChanged is called from outside of a managed thread. This is to warn you that you are doing "Blazor things" in a thread that wasn't given to you by Blazor.

If you do InvokeAsync(StateHasChanged) then you are still updating your component's state from a thread that Blazor doesn't manage, but hiding the exception that should be raising a red flag to warn you that you likely have thread race-conditions in your code.

Instead, code that is executed from something like a Threading.Timer, Tasks.Timer, or a notification from a SignalR connection you have created yourself, then it should be written like this...

async Task TimerTicked()
{
await InvokeAsync(DoSomething);
}

Where DoSomething includes ALL of the code that interacts with your Blazor component. This ensures that only ONE thread is ever accessing your component's state at any one given moment.

Note that all user interaction, and invokes from JS into Blazor components are already automatically invoked in this way.

You only need to do it for code Blazor doesn't trigger.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions