Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix 'The current thread is not associated with the renderer's synchronization context'?

I am trying to change a string, being used for a title, in my blazor-server-side application. But I am having trouble getting the UI to update.

I tried using StateHasChanged(), but that didn't work so I looked around and found that on the FlightFinder Demo that was made, it has an OnChange event Action, so I am trying to implement that.

It works until I try to refresh the browser, then I am hit with this error

System.InvalidOperationException: 'The current thread is not associated with the renderer's synchronization context. Use Invoke() or InvokeAsync() to switch execution to the renderer's synchronization context when triggering rendering or modifying any state accessed during rendering.'

This is what I have:

private string _title = "TestSite";
public string Title => _title;

public event Action OnChange;

public void ChangePage(string pageName)
{
   _title = pageName;
   NotifyStateChanged();
}

private void NotifyStateChanged(int navigationType = 0)
{
   OnChange?.Invoke();
}

All I have to do is call ChangePage("some Page Title") and it works, unless as I mentioned I try to refresh.

I am just trying to change a string on one component that through another component, which doesn't sound all that crazy. If there is a better way to do titles or change things from other components, I would love to hear about it.

So, what can I do to make sure that m invoke method is on the correct thread? Or is there a different way to change the title that would be more effective?

Thank you in advance!

like image 935
DalTron Avatar asked Jun 06 '19 12:06

DalTron


3 Answers

I have just implemented a State Container like this and ran into the same error - but my service needs to be a singleton. So I found an example on the aspnetcore git that does exactly what the error message says to do. Call InvokeAsync -- not from your state container but when you try to change the state of your razor component.

https://github.com/dotnet/aspnetcore/blob/321db9d99f84cf7a67d453384292d9339de748d1/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor

So your state container doesn't need to change, just your component event handler does.

@code{
    protected override void OnInitialized()
    {
         _YourService.OnChange += OnMyChangeHandler;
    }

    public void Dispose()
    {
         _YourService.OnChange -= OnMyChangeHandler;
    }

    private async void OnMyChangeHandler(object sender, EventArgs e)
    {
        // InvokeAsync is inherited, it syncs the call back to the render thread
        await InvokeAsync(() => {
            DoStuff();
            StateHasChanged();
        });
    }
}

Now your service (if it's a singleton) can notify ALL your users at once! Think about all hoops we had to jump through in past to do this.

like image 197
bmiller Avatar answered Oct 23 '22 13:10

bmiller


I posted this first thing in the morning thinking that I wouldn't have the time to look into and thinking that by time someone was able to help me out, I would have found the time to look into it more. Though I have spent a couple of days going back and forth on this already.

I finally found this article that explains that what I am trying to do is called a State Container.

What they said is that I could inject the class as a singleton, which is what I was doing or a scoped service. Turns out all I needed to do was change it to a scoped service and it works great!

like image 22
DalTron Avatar answered Oct 23 '22 13:10

DalTron


no need sophisticated solution, Blazor working perfectly if you will update GUI in your event handler by

this.InvokeAsync(() => this.StateHasChanged());
like image 8
Viacheslav Avatar answered Oct 23 '22 14:10

Viacheslav