perf(adminui): cache the modals' DynamicComponent parameter dictionaries (R04 H2)

DriverConfigModal, DeviceModal and RawTagModal built a new parameter dictionary
and new EventCallbacks every render. ParameterDictionaryCache reuses the
dictionary while the values it carries are unchanged and rebuilds it with the
current value when one changes (a build-once cache would strand edits); the
callbacks are created once. Blazor still re-sets an EventCallback-bearing
child's parameters per parent render, so the forms' _lastParsed guards stay.

Refs #745
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
Joseph Doherty
2026-10-22 17:43:01 -04:00
parent 36e6ab590b
commit b0959f697b
5 changed files with 185 additions and 26 deletions
@@ -108,13 +108,19 @@
private bool _enabled = true;
private string _deviceConfigJson = "{}";
/// <summary>Parameters handed to the DynamicComponent-rendered device form. Rebuilt on every render —
/// see DriverConfigModal for why this must not be cached.</summary>
private Dictionary<string, object> _deviceFormParameters => new()
{
["DeviceConfigJson"] = _deviceConfigJson,
["DeviceConfigJsonChanged"] = EventCallback.Factory.Create<string>(this, v => _deviceConfigJson = v),
};
/// <summary>Parameters handed to the DynamicComponent-rendered device form. Cached on the value it
/// carries and rebuilt when that changes (never build-once: see DriverConfigModal), with the callback
/// created once (R04 H2).</summary>
private Dictionary<string, object> _deviceFormParameters => _deviceFormParameterCache.Get(
[_deviceConfigJson],
() => new()
{
["DeviceConfigJson"] = _deviceConfigJson,
["DeviceConfigJsonChanged"] = _deviceConfigJsonChanged ??=
EventCallback.Factory.Create<string>(this, v => _deviceConfigJson = v),
});
private readonly ParameterDictionaryCache _deviceFormParameterCache = new();
private EventCallback<string>? _deviceConfigJsonChanged;
private byte[] _rowVersion = [];
private string _parentDriverConfig = "{}";