Skip to content

Allow JS.InvokeAsync<IJSObjectReference> calls to return null/undefined #52070

@SteveSandersonMS

Description

@SteveSandersonMS

Discovered by @tlmii as an issue Aspire Dashboard, or in fact in the underlying Fluent UI library

Currently, if you do either of these:

  • (In .NET) JS.InvokeAsync<IJSObjectReference> to a JS function that returns null or undefined
  • (In JS) DotNet.createJSObjectReference and pass the value null or undefined

... you get an error: Cannot create a JSObjectReference from the value 'null' (or undefined)

This makes it inconvenient to call a JS function that may return null or undefined.

Workaround

On the .NET side, you can define a struct like:

    private struct MyResult
    {
        public IJSObjectReference? Value { get; set; }
    }

... and then call js.InvokeAsync<MyResult>(...), and then on the JS side you'd do:

    return {
        value: something ? DotNet.createJSObjectReference(something) : null
    };

... so that createJSObjectReference is only called conditionally on there being a value for it. On the .NET side, you can then check whether result.Value is null.

However this is all very inconvenient.

Proposal

At minimum, we should change DotNet.createJSObjectReference so that if the supplied value is null/undefined, that's not an error, and we just track the null/undefined value in the same way we track nonempty values. This would not be a breaking change, since it gave an exception before.

If we wanted to go further, we could extend IJSObjectReference to have a bool HasValue { get; } so the .NET side could distinguish null/undefined from other values. That is a bit more involved since (1) it's extending an interface which is breaking unless we add a default behavior, and it's unclear what the default behavior would be, and (2) of course we have to send more info across the wire. It would have the benefit that we wouldn't have to track the null/undefined value on the JS side, because the .NET side could be extended to no-op if you dispose when HasValue is false, and the outbound serializer on the .NET side could automatically supply null if HasValue is false (assuming we're OK with losing the distinction between null and undefined). Altogether I'm not sure all of this "going further" design is worth it, given what a niche case it is, and how it introduces nonobvious design pivots.

Metadata

Metadata

Assignees

Labels

area-blazorIncludes: Blazor, Razor ComponentsenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-blazor-jsinteropThis issue is related to JSInterop in Blazorhelp candidateIndicates that the issues may be a good fit for community to help with. Requires work from eng. team

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions