From f329e9c766e4c6a3959dcd7304aad1956e32c036 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Thu, 22 Jan 2026 12:30:16 +0100 Subject: [PATCH] fix: guard all RichEditor imperative methods against unmounted view (#196) Add isEditorReady() helper to check if editor.view.dom is mounted before accessing editor commands. This prevents the TipTap error "The editor view is not available" that occurred when PostViewer's draft loading called editor methods before the view was fully mounted. Co-authored-by: Claude --- src/components/editor/RichEditor.tsx | 52 ++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/components/editor/RichEditor.tsx b/src/components/editor/RichEditor.tsx index 947c911..ff808bc 100644 --- a/src/components/editor/RichEditor.tsx +++ b/src/components/editor/RichEditor.tsx @@ -528,15 +528,31 @@ export const RichEditor = forwardRef( }, }); + // Helper to check if editor view is ready (prevents "view not available" errors) + const isEditorReady = useCallback(() => { + return editor && editor.view && editor.view.dom; + }, [editor]); + // Expose editor methods useImperativeHandle( ref, () => ({ - focus: () => editor?.commands.focus(), - clear: () => editor?.commands.clearContent(), - getContent: () => editor?.getText({ blockSeparator: "\n" }) || "", + focus: () => { + if (isEditorReady()) { + editor?.commands.focus(); + } + }, + clear: () => { + if (isEditorReady()) { + editor?.commands.clearContent(); + } + }, + getContent: () => { + if (!isEditorReady()) return ""; + return editor?.getText({ blockSeparator: "\n" }) || ""; + }, getSerializedContent: () => { - if (!editor) + if (!isEditorReady() || !editor) return { text: "", emojiTags: [], @@ -545,32 +561,40 @@ export const RichEditor = forwardRef( }; return serializeContent(editor); }, - isEmpty: () => editor?.isEmpty ?? true, + isEmpty: () => { + if (!isEditorReady()) return true; + return editor?.isEmpty ?? true; + }, submit: () => { - if (editor) { + if (isEditorReady() && editor) { handleSubmit(editor); } }, insertText: (text: string) => { - editor?.commands.insertContent(text); + if (isEditorReady()) { + editor?.commands.insertContent(text); + } }, insertBlob: (blob: BlobAttachment) => { - editor?.commands.insertContent({ - type: "blobAttachment", - attrs: blob, - }); + if (isEditorReady()) { + editor?.commands.insertContent({ + type: "blobAttachment", + attrs: blob, + }); + } }, getJSON: () => { + if (!isEditorReady()) return null; return editor?.getJSON() || null; }, setContent: (json: any) => { // Check editor and view are ready before setting content - if (editor?.view?.dom && json) { - editor.commands.setContent(json); + if (isEditorReady() && json) { + editor?.commands.setContent(json); } }, }), - [editor, handleSubmit], + [editor, handleSubmit, isEditorReady], ); // Handle submit on Ctrl/Cmd+Enter