Skip to content

Commit

Permalink
fix(react): useEditor should not destroy still mounted instances (#…
Browse files Browse the repository at this point in the history
…5338)

This forces the editor to re-use the editor instance that existed prior to an unmount and remount of the same component.
This fixes a bug in `React.StrictMode` introduced with the latest performance updates by PR #5161
  • Loading branch information
nperez0111 committed Jul 13, 2024
1 parent db0d007 commit 1110280
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 2 deletions.
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

0 comments on commit 1110280

Please sign in to comment.