You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server应用中StateHasChanged无法更新UI问题求助

问题:Blazor Server中SignalR接收数据后UI不更新

我正在开发一个基于SignalR连接ASP.NET Core API的Blazor Server应用,该代码在WebAssembly环境中运行正常,但在Blazor Server中却无法正常工作。

我怀疑问题在于StateHasChanged()未生效——控制台能打印Console.WriteLine($"Company Name: {item.CompanyName}, Volumn: {item.Volume}");的输出,但调用StateHasChanged()后MarketData并未更新UI。我也尝试过使用InvokeAsync(() => StateHasChanged()),但仍无效。

以下是我的代码:

private HubConnection? hubConn;
private string? ConnectionStatusMessage;
public List<Market> MarketData = new List<Market>(); 
public List<Market> MarketReceivedData = new List<Market>(); 

private List<string> xSource;
private List<int> ySource;
private List<object> source; 

protected override async Task OnInitializedAsync()
{
    xSource = new List<string>();
    ySource = new List<int>();
    source = new List<object>();  

    await service.GetMarketEndpoint();
    await Start();
}

private async Task Start()
{
    hubConn = new HubConnectionBuilder().WithUrl("https://localhost:7193/marketdata").Build();
    await hubConn.StartAsync();
    if(hubConn.State == HubConnectionState.Connected )
        ConnectionStatusMessage = "Connection is established Successfully...";
    else
        ConnectionStatusMessage = "Connection is not established...";
}

private void MarketDataListener(string chartType)
{
    hubConn.On<List<Market>>("SendMarketStatusData", async (data) =>
    {
        MarketData = new List<Market>(); 
        foreach (var item in data)
        {
            Console.WriteLine($"Company Name: {item.CompanyName}, Volumn: {item.Volume}");
            xSource.Add(item.CompanyName);
            ySource.Add(item.Volume);
        }

        source.Add(ySource);
        source.Add(xSource);

        MarketData = data;

        StateHasChanged();

        await js.InvokeAsync<object>(chartType, source.ToArray());
        xSource.Clear();
        ySource.Clear();
    });
}

private void ReceivedMarketDataListener()
{
    hubConn.On<List<Market>>("CommunicateMarketData", (data) =>
    {
        MarketReceivedData = data;
        StateHasChanged();           
    });
}

public async Task Dispose()
{
    await hubConn.DisposeAsync();
}

async Task generateLineChartTask()
{
    MarketDataListener("marketLineChart");
    ReceivedMarketDataListener();
    await service.GetMarketDataAsync();
}

async Task generateBarChartTask()
{
    MarketDataListener("marketBarChart"); 
    ReceivedMarketDataListener();
    await service.GetMarketDataAsync();
}

解决方案

1. 确保在同步上下文内执行状态更新

Blazor Server中,SignalR回调运行在后台线程,必须通过InvokeAsync切换回组件的同步上下文才能正确触发UI更新。把所有StateHasChanged()替换为:

await InvokeAsync(StateHasChanged);

比如修改ReceivedMarketDataListener中的回调:

hubConn.On<List<Market>>("CommunicateMarketData", async (data) =>
{
    MarketReceivedData = data;
    await InvokeAsync(StateHasChanged);           
});

2. 避免重复注册SignalR监听

你的generateLineChartTask和generateBarChartTask每次调用都会重复注册相同的SignalR事件监听,导致多次触发回调、状态混乱。应该只注册一次监听,比如在Start方法中完成:

修改后的Start方法:

private string currentChartType = "marketLineChart"; // 新增变量存储当前图表类型

private async Task Start()
{
    hubConn = new HubConnectionBuilder().WithUrl("https://localhost:7193/marketdata").Build();

    // 只注册一次SendMarketStatusData监听
    hubConn.On<List<Market>>("SendMarketStatusData", async (data) =>
    {
        MarketData = new List<Market>(); 
        foreach (var item in data)
        {
            Console.WriteLine($"Company Name: {item.CompanyName}, Volumn: {item.Volume}");
            xSource.Add(item.CompanyName);
            ySource.Add(item.Volume);
        }

        source.Add(ySource);
        source.Add(xSource);

        MarketData = data;

        await InvokeAsync(StateHasChanged);

        await js.InvokeAsync<object>(currentChartType, source.ToArray());
        xSource.Clear();
        ySource.Clear();
    });

    // 只注册一次CommunicateMarketData监听
    hubConn.On<List<Market>>("CommunicateMarketData", async (data) =>
    {
        MarketReceivedData = data;
        await InvokeAsync(StateHasChanged);           
    });

    await hubConn.StartAsync();
    ConnectionStatusMessage = hubConn.State == HubConnectionState.Connected 
        ? "Connection is established Successfully..." 
        : "Connection is not established...";
}

然后修改生成图表的方法,仅切换图表类型并请求数据:

async Task generateLineChartTask()
{
    currentChartType = "marketLineChart";
    await service.GetMarketDataAsync();
}

async Task generateBarChartTask()
{
    currentChartType = "marketBarChart";
    await service.GetMarketDataAsync();
}

3. 使用ObservableCollection优化集合更新

如果UI直接绑定集合内容,用ObservableCollection<Market>替代List<Market>,它会自动通知UI集合变化,无需手动调用StateHasChanged():

using System.Collections.ObjectModel;

public ObservableCollection<Market> MarketData = new ObservableCollection<Market>(); 
public ObservableCollection<Market> MarketReceivedData = new ObservableCollection<Market>(); 

更新集合时不要直接赋值,改为清空后添加元素:

// 替换 MarketData = data;
MarketData.Clear();
foreach(var item in data)
{
    MarketData.Add(item);
}

4. 验证连接状态与生命周期

确保组件在Dispose时正确释放SignalR连接,避免无效连接导致回调不触发:

public async ValueTask DisposeAsync()
{
    if(hubConn != null)
    {
        await hubConn.DisposeAsync();
    }
}

内容的提问来源于stack exchange,提问作者Happy Development

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 11:15:38