MudChipSet: Only re-render chips whose selection changed
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:
| chips | before | after |
|---|---|---|
| 50 | 203 renders | 107 |
| 200 | 803 renders | 407 |
Driving a real click through the DOM instead, measuring allocation:
| chips | before | after |
|---|---|---|
| 50 | 36.5 MB, 62 ms | 12.7 MB, 21 ms |
| 200 | 501 MB, 299 ms | 169 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.