Aspnetcore: Reconsider static JSRuntime.Current

Created on 18 Jan 2019  路  5Comments  路  Source: dotnet/aspnetcore

JSRuntime.Current is misleading. People commonly write code like this:

public override void OnInit()
{
    StocksService.OnStockTickerUpdated += stockUpdate =>
    {
        JSRuntime.Current.InvokeAsync<object>(
            "handleTickerChanged", stockUpdate.symbol, stockUpdate.price);
    };
}

This is fine for client-side code, but when executing on the server, which user's browser is it sending the handleTickerChanged call to? The developer probably thinks it's the user whose browser is rendering the component that contains this call, but that's not true. In fact, it depends on the async context in which the OnStockTickerUpdated event fires - maybe it's some other user who caused the event, or maybe it's nobody at all. When testing locally there's a good chance it seems to work, but the problem of sending the call to the wrong user only surfaces in production when there is more than one user.

The underlying problem is having a static JSRuntime.Current. If, instead, you could only acquire the IJSRuntime from DI, then each component would get and store the right instance for its user, and the call would always be sent to the right user. It's already possible to get the IJSRuntime from DI, but people don't realise that's what they should do so they continue to use JSRuntime.Current.

Proposal: Just remove the static JSRuntime.Current, so you can only get it from DI.

Drawback: in the pure client-side Blazor scenario, this complicates things for library code that doesn't necessarily have access to DI. It forces the developer to obtain the IJSRuntime from DI and pass it into the library somehow. However I still think this is the right thing to do, because if libraries are ever going to work in both client and server code, they have to let the developer pass the IJSRuntime into them.

Done area-blazor enhancement

Most helpful comment

I support this 馃槵

All 5 comments

cc @danroth27 @rynowak @javiercn

I support this 馃槵

@SteveSandersonMS What is the recommended way for injecting IJSRuntime if the component does not have a .cshtml file, e.g. when it relies on dynamic content by overriding BuildRenderTree and thus cannot use the @inject helper?

@wicked-sick You can just put [Inject] IJSRuntime JSRuntime { get; set; } and that should do it.

As noted here, for preview3, JSRuntime.Current is internal. We could take another stab at it as part of https://github.com/aspnet/AspNetCore-Internal/issues/1678 (I've noted it as a thing to do)

Was this page helpful?
0 / 5 - 0 ratings