From 6b9eb5c8febfbef14466dd1a042da239fe765f6c Mon Sep 17 00:00:00 2001 From: Pooria Shariatzadeh Date: Mon, 29 Jun 2026 14:29:17 +0330 Subject: [PATCH] fix(SbMarkdownEditor): improve disposal handling and error management during editor destruction - Introduce a _disposed flag to prevent operations on disposed instances. - Enhance error handling in DisposeAsync and editor destruction methods to log exceptions without crashing. - Ensure proper checks before invoking interop methods to maintain stability during component lifecycle events. --- .../Forms/SbMarkdownEditor.razor.cs | 86 +++++++++++++++++-- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/src/SufiChain.SufiBlazor/Components/Forms/SbMarkdownEditor.razor.cs b/src/SufiChain.SufiBlazor/Components/Forms/SbMarkdownEditor.razor.cs index 92d403a..d3b4202 100644 --- a/src/SufiChain.SufiBlazor/Components/Forms/SbMarkdownEditor.razor.cs +++ b/src/SufiChain.SufiBlazor/Components/Forms/SbMarkdownEditor.razor.cs @@ -58,19 +58,29 @@ public partial class SbMarkdownEditor : ComponentBase, IAsyncDisposable private bool _lastIncludeDefaultToolbarItems; private List _toolbarItems = new(); private bool _useFallback; + private bool _disposed; protected bool IsReady { get; private set; } protected override async Task OnAfterRenderAsync(bool firstRender) { + if (_disposed) + { + return; + } + if (firstRender) { _interop = new SbMarkdownEditorInterop(JSRuntime); - _dotNetRef = DotNetObjectReference.Create(this); await LoadToolbarItemsAsync(); await InitializeEditorAsync(); + if (_disposed) + { + return; + } + _lastRenderedValue = Value; _lastRenderedOriginal = OriginalValue; _lastRenderedSuggested = SuggestedValue; @@ -149,11 +159,18 @@ public partial class SbMarkdownEditor : ComponentBase, IAsyncDisposable private async Task InitializeEditorAsync() { + if (_disposed || _interop == null) + { + return; + } + try { + _dotNetRef ??= DotNetObjectReference.Create(this); + if (IsDiffReview) { - _editorId = await _interop!.InitDiffReviewAsync(_diffContainer, _dotNetRef!, new SbMarkdownDiffInitOptions + _editorId = await _interop.InitDiffReviewAsync(_diffContainer, _dotNetRef, new SbMarkdownDiffInitOptions { EditorId = _elementId, Original = OriginalValue, @@ -164,14 +181,19 @@ public partial class SbMarkdownEditor : ComponentBase, IAsyncDisposable } else { - await _interop!.EnsureAssetsAsync(new SbMarkdownAssetOptions + await _interop.EnsureAssetsAsync(new SbMarkdownAssetOptions { EnableMermaid = EnableMermaid, EnableHighlight = EnableHighlight, HighlightTheme = HighlightTheme }); - _editorId = await _interop.InitEditorAsync(_textarea, _dotNetRef!, new SbMarkdownEditorInitOptions + if (_disposed) + { + return; + } + + _editorId = await _interop.InitEditorAsync(_textarea, _dotNetRef, new SbMarkdownEditorInitOptions { EditorId = GetEditorId(), Value = Value, @@ -189,6 +211,11 @@ public partial class SbMarkdownEditor : ComponentBase, IAsyncDisposable }); } + if (_disposed) + { + return; + } + IsReady = !string.IsNullOrEmpty(_editorId); _useFallback = !IsReady; } @@ -201,6 +228,11 @@ public partial class SbMarkdownEditor : ComponentBase, IAsyncDisposable private async Task ReinitializeEditorAsync() { + if (_disposed) + { + return; + } + if (_editorId != null && _interop != null) { await _interop.DestroyEditorAsync(_editorId); @@ -210,8 +242,18 @@ public partial class SbMarkdownEditor : ComponentBase, IAsyncDisposable IsReady = false; _useFallback = false; await InvokeAsync(StateHasChanged); + if (_disposed) + { + return; + } + await LoadToolbarItemsAsync(); await InitializeEditorAsync(); + if (_disposed) + { + return; + } + _lastRenderedDiffMode = IsDiffReview; _lastRenderedValue = Value; _lastRenderedOriginal = OriginalValue; @@ -388,14 +430,46 @@ public partial class SbMarkdownEditor : ComponentBase, IAsyncDisposable public async ValueTask DisposeAsync() { + _disposed = true; + if (_editorId != null && _interop != null) { - await _interop.DestroyEditorAsync(_editorId); + try + { + await _interop.DestroyEditorAsync(_editorId); + } + catch (JSDisconnectedException ex) + { + System.Diagnostics.Debug.WriteLine($"[SbMarkdownEditor] DestroyEditorAsync JSDisconnected: {ex.Message}"); + } + catch (ObjectDisposedException ex) + { + System.Diagnostics.Debug.WriteLine($"[SbMarkdownEditor] DestroyEditorAsync ObjectDisposed: {ex.Message}"); + } + catch (System.Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[SbMarkdownEditor] DestroyEditorAsync error: {ex}"); + } } if (_interop != null) { - await _interop.DisposeAsync(); + try + { + await _interop.DisposeAsync(); + } + catch (JSDisconnectedException ex) + { + System.Diagnostics.Debug.WriteLine($"[SbMarkdownEditor] Interop DisposeAsync JSDisconnected: {ex.Message}"); + } + catch (ObjectDisposedException ex) + { + System.Diagnostics.Debug.WriteLine($"[SbMarkdownEditor] Interop DisposeAsync ObjectDisposed: {ex.Message}"); + } + catch (System.Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[SbMarkdownEditor] Interop DisposeAsync error: {ex}"); + } } _dotNetRef?.Dispose();