Skip to content

Commit

Permalink
⚡ refactor(Sortable): add a detection mechanism for potential frequen…
Browse files Browse the repository at this point in the history
…t rendering
  • Loading branch information
capdiem committed Aug 24, 2024
1 parent 8c6ef3b commit 63dc821
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 6 deletions.
2 changes: 0 additions & 2 deletions src/Masa.Blazor/Components/Sortable/MSortable.razor
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
@namespace Masa.Blazor
@inherits MSortableProviderBase<TItem>
@using Masa.Blazor.Components.Sortable
@typeparam TItem
@inject SortableJSModule SortableJSModule

<MElement Class="@GetClass()"
Style="@GetStyle()"
Expand Down
11 changes: 7 additions & 4 deletions src/Masa.Blazor/Components/Sortable/MSortableProviderBase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Masa.Blazor.Components.Sortable;
using Masa.Blazor.Utils;

namespace Masa.Blazor;

Expand Down Expand Up @@ -181,6 +182,8 @@ public bool Disabled
private DotNetObjectReference<SortableJSInteropHandle>? _sortableJSInteropHandle;
private SortableJSObjectReference? _jsObjectReference;

private RenderRateLimiter? _renderRateLimiter;

protected abstract string ContainerSelector { get; }

protected override void OnInitialized()
Expand All @@ -189,6 +192,8 @@ protected override void OnInitialized()

_prevItems = Items;
_prevItemKeys = GetItemKeys();
_renderRateLimiter = new RenderRateLimiter(
$"{ComponentName} is rendering too frequently. Check the Items parameter to ensure its reference doesn't change frequently.");

_sortableJSInteropHandle = DotNetObjectReference.Create(new SortableJSInteropHandle(this));
}
Expand All @@ -200,10 +205,7 @@ protected override void RegisterWatchers(PropertyWatcher watcher)
base.RegisterWatchers(watcher);

watcher.Watch<bool>(nameof(Disabled),
val =>
{
_jsObjectReference?.InvokeVoidAsync("option", "disabled", val);
})
val => { _jsObjectReference?.InvokeVoidAsync("option", "disabled", val); })
.Watch<List<string>>(nameof(Order), val => { _ = _jsObjectReference?.SortAsync(val, false); });
}

Expand All @@ -218,6 +220,7 @@ protected override void OnParametersSet()
{
_prevItems = Items;
_prevItemKeys = GetItemKeys();
_renderRateLimiter?.RecordRender();
RefreshOrder();
}
else
Expand Down
51 changes: 51 additions & 0 deletions src/Masa.Blazor/Utils/RenderRateLimiter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace Masa.Blazor.Utils;

/// <summary>
/// The RenderRateLimiter class is used to limit the frequency of render operations.
/// It throws an InvalidOperationException if the render count exceeds the specified threshold within one second.
/// </summary>
public class RenderRateLimiter
{
private int _renderCount;
private long _renderStartTimestamp;
private readonly string _errorMessage;
private readonly int _threshold;

/// <summary>
/// Initializes a new instance of the RenderRateLimiter class.
/// </summary>
/// <param name="errorMessage">The error message to be thrown when the render count exceeds the threshold.</param>
/// <param name="threshold">The maximum number of renders allowed within one second. Default is 20.</param>
public RenderRateLimiter(string errorMessage, int threshold = 20)
{
_errorMessage = errorMessage;
_threshold = threshold;
}

/// <summary>
/// Records a render operation. If the render count exceeds the threshold within one second, an InvalidOperationException is thrown.
/// </summary>
public void RecordRender()
{
if (_renderCount == 0)
{
_renderStartTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
_renderCount++;
}
else
{
_renderCount++;
var now = DateTimeOffset.Now.ToUnixTimeSeconds();
if (now - _renderStartTimestamp > 1)
{
var copy = _renderCount;
_renderCount = 0;

if (copy > _threshold)
{
throw new InvalidOperationException(_errorMessage);
}
}
}
}
}

0 comments on commit 63dc821

Please sign in to comment.