b0959f697b
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
51 lines
2.3 KiB
C#
51 lines
2.3 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared;
|
|
|
|
/// <summary>
|
|
/// Caches a <c>DynamicComponent</c> parameter dictionary (R04 H2) and rebuilds it only when one of the
|
|
/// values it carries changed. The modals used to build a new dictionary, and new
|
|
/// <c>EventCallback</c>s, on every render. A plain build-once cache is wrong: it would pin the JSON
|
|
/// captured at first render and silently strand every later edit — so the cache is keyed on the
|
|
/// current values, and the callbacks it hands out are created once by the caller and reused.
|
|
/// <para>Note the limit: Blazor's change detection treats any <c>EventCallback</c> parameter as
|
|
/// possibly changed, so a hosted form still sees <c>SetParametersAsync</c> on each parent render. The
|
|
/// per-form <c>_lastParsed</c> guards remain load-bearing; this removes the per-render allocations and
|
|
/// gives the child a stable dictionary and callback identity.</para>
|
|
/// </summary>
|
|
public sealed class ParameterDictionaryCache
|
|
{
|
|
private object?[]? _keyValues;
|
|
private Dictionary<string, object>? _cached;
|
|
|
|
/// <summary>Returns the cached dictionary when <paramref name="keyValues"/> equal the previous call's,
|
|
/// otherwise builds, caches and returns a new one.</summary>
|
|
/// <param name="keyValues">The values the dictionary carries (compared with <see cref="object.Equals(object, object)"/>).</param>
|
|
/// <param name="build">Builds the dictionary from the current state.</param>
|
|
/// <returns>The parameter dictionary.</returns>
|
|
public Dictionary<string, object> Get(object?[] keyValues, Func<Dictionary<string, object>> build)
|
|
{
|
|
if (_cached is not null && _keyValues is not null && Same(_keyValues, keyValues)) return _cached;
|
|
|
|
_cached = build();
|
|
_keyValues = (object?[])keyValues.Clone();
|
|
return _cached;
|
|
}
|
|
|
|
/// <summary>Drops the cached dictionary (e.g. on modal close) so the next <see cref="Get"/> rebuilds.</summary>
|
|
public void Reset()
|
|
{
|
|
_cached = null;
|
|
_keyValues = null;
|
|
}
|
|
|
|
private static bool Same(object?[] a, object?[] b)
|
|
{
|
|
if (a.Length != b.Length) return false;
|
|
for (var i = 0; i < a.Length; i++)
|
|
{
|
|
if (!Equals(a[i], b[i])) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|