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

13,825

Solution 1

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!

Solution 2

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.

Solution 3

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

this.InvokeAsync(() => this.StateHasChanged());
Share:
13,825

Related videos on Youtube

DalTron
Author by

DalTron

Updated on May 25, 2022

Comments

  • DalTron
    DalTron almost 2 years

    I am trying to change 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 like 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 changing 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!

  • DalTron
    DalTron almost 5 years
    After briefly reading up on scoped vs singleton, this makes perfect sense. I am still going to keep my question up because I don't think blazor has enough questions and answers and this might help someone at some point.
  • nam
    nam about 4 years
    does it work with public event Action OnChange; ?
  • Nik P
    Nik P about 4 years
    @bmiller really appreciate your answer on this, it got me out of the weeds about a month ago. I referenced you in this thread: stackoverflow.com/questions/61354233/…
  • Rich Armstrong
    Rich Armstrong almost 4 years
    I'm unsure why the lambda expression is needed. This worked for me: await InvokeAsync(StateHasChanged);
  • bmiller
    bmiller almost 4 years
    You're right the lamda isn't required. However I think it makes it more obvious what's happening and most likely your "DoStuff()" code might need to run inside the Invoke context as well.
  • bmiller
    bmiller almost 4 years
    @nam your service simply invokes the event as usual; OnChange?.Invoke(this, EventArgs.Empty); If your service is a singleton every page/user should get the event.
  • Peter Duniho
    Peter Duniho almost 4 years
    How is this any different from the answer the OP already got? (Never mind that based on their self-accepted answer, they clearly prefer changing the service type rather than using InvokeAsync()). I don't find this answer to be a useful contribution at all. It adds nothing to the information already present.
  • Peter Duniho
    Peter Duniho almost 4 years
    It's not DalTron's answer that I'm talking about. Your "really working code" is literally identical to the code posted in bmiller's answer, except for the slight difference that in the other answer, they actually explain the code.
  • qd0r
    qd0r over 3 years
    @bmiller But how does the code in the OnChange Action in your sample code look like? OnChange?.Invoke(); still produces the Exception but somehow the event must be fired. :(
  • bmiller
    bmiller over 3 years
    @qd0r the exception may be that you're doing too much in the event. I just edited my handler to include the DoStuff function inside the InvokeAsync. I think ultimately just StateHasChanged() must be called inside the InvokeAsync, but to be careful just put everything in it. Note the event is fired from in the service - maybe in response to another user action.
  • Payedimaunt
    Payedimaunt over 2 years
    this solution dosen't resolve the problem
  • Julian Dormon
    Julian Dormon over 2 years
    That worked for me! Thx
  • Greg Gum
    Greg Gum about 2 years
    I voted up this answer because @Vaicheslav has exactly 666 points.
  • clamchoda
    clamchoda about 2 years
    @PeterDuniho it doesn't need the await operator and the calling function doesn't need to change to async.