In this lesson, you will learn how to bridge the gap between real-time SignalR streams and Blazor WebAssembly’s component state to create fluid, reactive user interfaces.
Binding SignalR to Component State
In Blazor WebAssembly, the client-side UI runs in the browser. When a SignalR message arrives, the framework doesn't automatically know that the UI needs to update. Because SignalR callbacks (like On("ReceiveMessage", ...)) often run outside of the Blazor rendering cycle, you must manually bridge the incoming data to the component’s private state and notify the renderer.
To do this effectively, you store your incoming data in a local collection and invoke StateHasChanged() to force the component to re-render based on the updated data.
Implementation Pattern
Here is how you wire a typical message handler inside a Blazor component’s OnInitializedAsync method:
private HubConnection? _hubConnection;
private List<string> _messages = new();
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"))
.Build();
_hubConnection.On<string>("ReceiveMessage", (message) =>
{
_messages.Add(message);
// Ensure UI updates occur on the Synchronization Context
InvokeAsync(StateHasChanged);
});
await _hubConnection.StartAsync();
}
Why InvokeAsync Matters
SignalR event handlers often execute on a background thread pool thread. Since Blazor components must be updated on the UI thread, calling StateHasChanged directly inside an On handler will frequently lead to race conditions or silent failures. By wrapping the call in InvokeAsync(StateHasChanged), you marshal the execution back to the Blazor renderer’s synchronization context, ensuring the UI stays perfectly in sync with the incoming data stream.
This approach turns your component into a reactive observer of the SignalR hub. As the stream pushes updates, your component state updates locally, and the Blazor diffing engine efficiently updates only the parts of the DOM that changed.
Key Takeaway: Always use InvokeAsync(StateHasChanged) when updating component state from SignalR callbacks to ensure your UI updates occur on the correct thread.