Skip to content

NumericInput: no way to opt out of the JS interop layer #464

Description

@mathewtaylor

Split out of #328 (@leoneljerez), which bundled several unrelated things. This is the headline request from it: a numeric input that does not round-trip through JS.

The request

BbNumericInput has no mode that avoids JS interop. The reporter hit this building an Excel-like editing surface on WebAssembly — many numeric editors live at once — and measured a noticeable slowdown against MudBlazor's equivalent, which is pure C#.

They asked whether a Primitives-layer numeric input exists that strips the JS, or an attribute to disable it. Neither does.

What the JS layer actually costs

Per instance, on first render: a module import (cached after the first component), a DotNetObjectReference, and four addEventListener registrations — input, blur, focus, keydown — plus wheel when EnableWheelStep is set. Each instance also needs its dispose interop call on teardown.

Per keystroke: sanitizeInput runs in JS, then dotNetRef.invokeMethodAsync('JsOnInput', value). blur, focus and the stepping keys each round-trip separately.

DisableDebounce is not the escape hatch it looks like. It does not reduce interop — it removes the setTimeout, so JsOnInput fires immediately on every keystroke rather than once after DebounceInterval (default 500ms). It exists to make bindings feel immediate, and it makes the call volume strictly worse. Worth stating plainly because it is the parameter anyone would reach for first, and the issue sat for months partly because nobody said so.

What a JS-free mode would have to give up or reimplement

The JS is not decorative — porting it to C# means reimplementing:

  • Input sanitisation with cursor preservation. Strips non-numeric characters as you type and restores the caret to the right offset, accounting for removed characters before it. Doing this from @oninput in C# means a round-trip to set SelectionStart, which is itself interop — so a naive port may not save anything.
  • Full-width character folding (foldFullWidth), which matters for CJK input methods.
  • Key stepping for Arrow/PageUp/PageDown/Home/End, currently handled by a JS keydown listener.
  • Wheel stepping, with its accumulator for trackpad momentum.

A @oninput-driven C# implementation is straightforward for value binding and stepping, but the caret behaviour during sanitisation is the part that will decide whether it is actually usable.

Open questions

  • Which layer? The reporter asked for a Primitives version. That fits the library's split — headless and unstyled, consumer supplies behaviour — but the sanitisation is behaviour, not styling, so it is not obvious the primitive is the right home.
  • New component or a parameter? A DisableJsInterop-style flag on the existing component keeps one API but means two divergent code paths with subtly different typing behaviour, which is a support burden. A separate component is honest about the trade-off but duplicates surface.
  • Is the cost per-instance or per-keystroke? Worth measuring before designing. If the dominant cost is per-instance setup in a grid of 200 editors, the fix might be shared listener delegation rather than removing JS.

Related

The same reporter noted that if this is not feasible, a performant editable DataGrid would address their underlying need. That is a larger piece of work but the same motivation — many simultaneous editors — and worth keeping in mind when scoping this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    consideringFor ideas and requests we’re actively evaluating but haven’t committed to yet.enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions