Skip to content
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

fix(react): useEditor should not destroy instances that are still mounted #5338

Merged
merged 2 commits into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clever-mice-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tiptap/react": patch
---

Fixes strict mode accidentally destroying the editor instance
10 changes: 9 additions & 1 deletion demos/src/Examples/Performance/React/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function EditorInstance({ shouldOptimizeRendering }) {
)
}

export default () => {
const EditorControls = () => {
const [shouldOptimizeRendering, setShouldOptimizeRendering] = React.useState(true)

return (
Expand Down Expand Up @@ -128,3 +128,11 @@ export default () => {
</>
)
}

export default () => {
return (
<React.StrictMode>
<EditorControls />
</React.StrictMode>
)
}
12 changes: 11 additions & 1 deletion packages/react/src/useEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export function useEditor(
options: UseEditorOptions = {},
deps: DependencyList = [],
): Editor | null {
const isMounted = useRef(false)
const [editor, setEditor] = useState(() => {
if (options.immediatelyRender === undefined) {
if (isSSR || isNext) {
Expand Down Expand Up @@ -220,12 +221,21 @@ export function useEditor(
* only be called when the component is removed from the DOM, since it has no deps.
* */
useEffect(() => {
isMounted.current = true
return () => {
isMounted.current = false
if (editor) {
// We need to destroy the editor asynchronously to avoid memory leaks
// because the editor instance is still being used in the component.

setTimeout(() => (editor.isDestroyed ? null : editor.destroy()))
setTimeout(() => {
// re-use the editor instance if it hasn't been destroyed yet
// and the component is still mounted
// otherwise, asynchronously destroy the editor instance
if (!isMounted.current && !editor.isDestroyed) {
editor.destroy()
}
})
}
}
}, [])
Expand Down