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.
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)
Most helpful comment
I support this 馃槵