我正在尝试创建一个简单的搜索输入控件,具有基于计时器的去抖功能。
Search.razor
:
<input type="search" placeholder="Search" @oninput=@((e) => SearchChanged(e)) />
@code {
[Parameter]
public EventCallback<string> OnSearchChanged { get; set; }
private System.Timers.Timer? _debounceTimer;
string Text = string.Empty;
public void SearchChanged(ChangeEventArgs e)
{
Text = e.Value == null ? string.Empty : e.Value.ToString()!;
// Debounce search
_debounceTimer?.Stop();
_debounceTimer?.Dispose();
_debounceTimer = new System.Timers.Timer(200);
_debounceTimer.Elapsed += DebounceTimerElapsed;
_debounceTimer.Enabled = true;
_debounceTimer.Start();
}
private void DebounceTimerElapsed(object? sender, EventArgs e)
{
_debounceTimer?.Dispose();
OnSearchChanged.InvokeAsync(Text); // <<< Dispatcher exception
}
}
我认为在这个组件中包含去抖动功能是有意义的,这样我就不必在每个父级上重复它。但我似乎无法使用 EventCallback 让父组件知道计时器已过。它抛出了臭名昭著的The current thread is not associated with the Dispatcher
异常。
Blazor University 上的此页面解释了非 UI 事件(例如Timer.Elapsed
在另一个线程上生成)。计时器确实是罪魁祸首,因为显然如果我OnSearchChanged.InvokeAsync()
直接从SearchChanged
. 但InvokeAsync()
在这种情况下似乎没有同步回 UI 线程。但我认为应该有一种简单的方法来完成这项工作。
有没有一种(简单)方法让父组件知道子组件中的计时器已过?
尝试从 Blazor 中的非 UI 线程更新 UI 时,您遇到的错误消息很常见。这种情况经常发生在异步操作或计时器的事件处理程序中,就像您在
System.Timers.Timer
. 由于计时器的 Elapsed 事件在线程池线程上运行,而不是在 Blazor Dispatcher 线程上运行,因此尝试修改组件状态会直接导致此错误。要解决此问题,您需要确保 UI 更新逻辑在 Blazor Dispatcher 线程上执行。您可以通过使用 InvokeAsync 方法将回调编组回 Blazor 组件的上下文来实现此目的。以下是您可以修改搜索方法以使用的方法
InvokeAsync
:(假设您将重复使用该组件并且想要
@bind
使用它。去抖搜索.razor
Index.razor或任何其他父级