-
-
Notifications
You must be signed in to change notification settings - Fork 717
fix: useEditorDOMElement hook
#2619
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
58457e2
4811525
70b8075
c20d1c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,43 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| import { useEffect, useState } from "react"; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import { useBlockNoteEditor } from "./useBlockNoteEditor.js"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { useEditorState } from "./useEditorState.js"; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||
| * Returns the editor's DOM element reactively. | ||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||
| * `editor.domElement` is gated behind TipTap's `isInitialized` flag, which is | ||||||||||||||||||||||||||||||||||||||||||||
| * set in a `setTimeout(0)` after mount. A plain read of `editor.domElement` | ||||||||||||||||||||||||||||||||||||||||||||
| * during the first render (or effect) will see `undefined`, and no transaction | ||||||||||||||||||||||||||||||||||||||||||||
| * fires after `isInitialized` flips to notify subscribers. | ||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||
| * This hook uses `useEditorState` to pick up changes that coincide with | ||||||||||||||||||||||||||||||||||||||||||||
| * transactions (e.g. remounts), and supplements it with a TipTap `create` | ||||||||||||||||||||||||||||||||||||||||||||
| * event listener to handle the initial mount timing. | ||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||
| export function useEditorDOMElement() { | ||||||||||||||||||||||||||||||||||||||||||||
| const editor = useBlockNoteEditor<any, any, any>(); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Handle initial mount timing. TipTap sets isInitialized synchronously | ||||||||||||||||||||||||||||||||||||||||||||
| // right after emitting the "create" event, so by the time React processes | ||||||||||||||||||||||||||||||||||||||||||||
| // this state update, editor.domElement is available. | ||||||||||||||||||||||||||||||||||||||||||||
| const [, setInitialized] = useState(!editor.headless); | ||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||
| if (!editor.headless) { | ||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| const handler = () => setInitialized(true); | ||||||||||||||||||||||||||||||||||||||||||||
| editor._tiptapEditor.on("create", handler); | ||||||||||||||||||||||||||||||||||||||||||||
| return () => { | ||||||||||||||||||||||||||||||||||||||||||||
| editor._tiptapEditor.off("create", handler); | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Force a re-render on every
💡 Suggested fix- const [, setInitialized] = useState(!editor.headless);
+ const [, bumpMountVersion] = useState(0);
useEffect(() => {
if (!editor.headless) {
return;
}
- const handler = () => setInitialized(true);
+ const handler = () => bumpMountVersion((v) => v + 1);
editor._tiptapEditor.on("create", handler);
return () => {
editor._tiptapEditor.off("create", handler);
};
}, [editor]);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||
| }, [editor]); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Re-evaluate editor.domElement on every render (including the one | ||||||||||||||||||||||||||||||||||||||||||||
| // triggered by setInitialized above) and on every transaction. | ||||||||||||||||||||||||||||||||||||||||||||
| return useEditorState({ | ||||||||||||||||||||||||||||||||||||||||||||
| editor, | ||||||||||||||||||||||||||||||||||||||||||||
| selector: (ctx) => ctx.editor?.domElement, | ||||||||||||||||||||||||||||||||||||||||||||
| equalityFn: (a, b) => a === b, | ||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t ignore the explicit
editorargument in this exported hook.The implementation now binds keyboard listeners using context (
useEditorDOMElement()), while Line 8 still accepts an editor argument. That can desync targets in multi-editor/custom usage and is a behavioral regression for consumers passing an explicit editor.💡 Suggested fix
And update
useEditorDOMElementto accept an optional editor override:🤖 Prompt for AI Agents