Blazor component removed from DOM before disposal causes js interop to fail
See original GitHub issueI’ve already posted this on SO without success.
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
My Blazor component has some associated JavaScript, which performs (async) animations.
MyComponent.razor
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (someCondition & jsModule != null)
await jsModule.InvokeVoidAsync("startAnimation", "#my-component");
}
public async ValueTask DisposeAsync()
{
if (jsModule != null)
await jsModule.InvokeVoidAsync("stopAnimationPrematurely", "#my-component");
}
MyComponent.razor.js
export function startAnimation(id) {
let element = document.getElementById(id);
element.addEventListener(
'animationend',
function() { element.classList.remove("cool-animation") },
{ once: true }
);
element.classList.add("cool-animation");
}
export function stopAnimationPrematurely(id) {
let element = document.getElementById(id); // fails here
element.removeEventListener(
'animationend',
function() { element.classList.remove("cool-animation") },
{ once: true }
);
element.classList.remove("cool-animation");
}
As you can see, the animation cleans up after itself (via { once: true }
).
However when the user clicks to a different page or component - and thus the blazor component is destroyed - there could be an animation in progress. If I don’t remove the js event listener then I’ll get a memory leak. So in DisposeAsync()
I invoke js cleanup code which explicitly calls removeEventListener()
.
The problem is that by the time the js code runs, the component is already destroyed - so the DOM element is missing, the id is invalid, and thus the js cleanup fails (and throws).
Expected Behavior
This is very surprising. There is a race condition between the various lifecycle methods and disposal.
In MudBlazor they also encountered this and introduced all sorts of locking workarounds.
Steps To Reproduce
See code above.
Exceptions (if any)
JS errors: document.getElementById(id)
fails because id
refers to an element no longer part of the DOM.
.NET Version
6.0.404
Anything else?
Thanks.
Issue Analytics
- State:
- Created 9 months ago
- Comments:6 (6 by maintainers)
@lonix1 … It’s already covered in both docs at …
However … @javiercn …
I have it down for Blazor Server only scenarios. Currently, the coverage focuses on cleanup after the SignalR circuit is destroyed. Seems like I should broaden it out to cover Blazor WASM (and Server, really) for the element is MIA from the DOM 💥 case (… and perhaps mention the renderer is gone for Server, too). Is that correct?
@guardrex Yes I should have mentioned this was for blazor wasm.