-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Description
@SteveSandersonMS hijacking the original post... see the comment below for a more concise statement about what we need to fix.
Describe the bug
A clear and concise description of what the bug is.
I need to process client event on server but after some timeout.
Scenario:
- user types in input
- user removes focus from input
- some timeout passes, if user do not return focus to input -> js code calls server method of DotNetObjectRef , created from an instance of server component (ComponentBase deriven class).
To Reproduce
Problem happens when user types something in input and click some navigation link.
In this case following happens:
- user types in input
- user clicks navigation link -> it removes focus from input
- previous component disposes (and disposes the DotNetObjectRef object)
- some timeout passes -> js code calls server method of disposed instance of server component (and disposed DotNetObjectRef object created from that disposed component)
- client application gets "There is no tracked object with id"... "Perhaps the DotNetObjectRef instance was already disposed." message and spots working.
Expected behavior
Since the DotNetObjectRef object and the target component are disposed operation can't be completed, but there is a way for me to ignore it, because i do not need it any more in this scenario.
Simplest project to reproduce
Counter2.razor
@page "/counter2"
@inherits SomeNameSpace.Counter2Base
<input @ref="@input" />
<span>@Text</span>
Counter2.razor.cs
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;
namespace SomeNameSpace
{
public partial class Counter2Base : ComponentBase, IDisposable
{
public ElementRef input;
public string Text;
[Inject] IJSRuntime JsRuntime { get; set; }
[Inject] IComponentContext ComponentContext { get; set; }
object objReg;
object ObjReg {
get {
if (objReg == null)
objReg = DotNetObjectRef.Create(this);
return objReg;
}
}
protected override void OnAfterRender()
{
SubscribeEvents();
}
public async void SubscribeEvents()
{
await JsRuntime.InvokeAsync<object>(
"exampleJsFunctions.SubscribeEvents", input, ObjReg);
}
[JSInvokable]
public void LostFocus(string text)
{
Text = text;
StateHasChanged();
}
public void Dispose()
{
var disposableObjReg = objReg as IDisposable;
if(disposableObjReg != null)
disposableObjReg.Dispose();
}
}
}
exampleJsInterop.js
window.exampleJsFunctions = {
SubscribeEvents: function (element, dotnetHelper) {
element.addEventListener('blur', function (e) {
window.setTimeout(function () {
dotnetHelper.invokeMethodAsync('LostFocus', e.target.value);
}, 1000);
});
},
};
- Navigate to "counter2" page
- Open console dev tools in browser to see the error message
- type something to the input and click some navigation link except "counter2" to leave this page
- error, app stops operating
Possible workaround?
What is the best strategy to workaround this?
-
I can remove the objReg disposing code (the object created by DotNetObjectRef). But who will be releasing it in this case?
-
I can create a static collection of that objReg objects, mark them with some "ReadyToDispose" flag instead of disposing, and dispose them after some safe timeout on server.
May be something also? Please suggest.
Thank you!