MudChipSet: Only re-render chips whose selection changed

Merged
DA
2mo ago
+55-2

Problem

MudChip.UpdateSelectionStateAsync ended with an unconditional StateHasChanged(), and MudChipSet calls it for every chip on every selection change (UpdateChipsAsync, UpdateSelectedValueAsync). Clicking one chip therefore rebuilt the whole set, so the cost of a single click grew with the number of chips.

Fix

Render only when the chip's own selected state actually changed. Nothing a chip renders depends on another chip, so the rest have nothing new to show. ParameterState.SetValueAsync already ignores an unchanged value, so only the render was being wasted.

One subtlety is worth calling out for review. MudChip.OnClickAsync set its own state directly and then relied on the set's sweep to render it:

await SelectedState.SetValueAsync(!SelectedState.Value);
await ChipSet.OnChipSelectedChangedAsync(this, SelectedState.Value);

So the unconditional render was the only thing painting the clicked chip. Guarding the sweep on its own leaves the clicked chip showing its old state, and ChipSet_SelectedValues_TwoWayBinding fails. The click now goes through the same helper, so a chip renders exactly when its own state changes and the sweep correctly finds nothing to do for it.

Numbers

Component renders counted from RenderBatch.UpdatedComponents on a bare Renderer, median of 5. Selecting one chip:

chipsbeforeafter
50203 renders107
200803 renders407

Driving a real click through the DOM instead, measuring allocation:

chipsbeforeafter
5036.5 MB, 62 ms12.7 MB, 21 ms
200501 MB, 299 ms169 MB, 123 ms

Those absolute figures are inflated because bUnit re-serialises the DOM on every render, and they grow faster than linearly for the same reason. Read them as a ratio; the component-render counts above are the trustworthy figure.