Skip to content

Commit

Permalink
fix(react): optimize useEditor and useEditorState to reduce numbe…
Browse files Browse the repository at this point in the history
…r of instances created while being performant #5432 (#5445)
  • Loading branch information
nperez0111 authored Aug 5, 2024
1 parent 84ebd51 commit 7c8889a
Show file tree
Hide file tree
Showing 10 changed files with 595 additions and 387 deletions.
12 changes: 12 additions & 0 deletions .changeset/smooth-rice-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@tiptap/react": patch
---

Optimize `useEditor` and `useEditorState` to reduce number of instances created while still being performant #5432

The core of this change is two-fold:
- have the effect run on every render (i.e. without a dep array)
- schedule destruction of instances, but bail on the actual destruction if the instance was still mounted and a new instance had not been created yet

It should plug a memory leak, where editor instances could be created but not cleaned up in strict mode.
As well as fixing a bug where a re-render, with deps, was not applying new options that were set on `useEditor`.
9 changes: 9 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ module.exports = {
node: true,
},
overrides: [
{
files: [
'./**/*.ts',
'./**/*.tsx',
'./**/*.js',
'./**/*.jsx',
],
extends: ['plugin:react-hooks/recommended'],
},
{
files: [
'./**/*.ts',
Expand Down
8 changes: 4 additions & 4 deletions demos/src/Commands/Cut/React/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import StarterKit from '@tiptap/starter-kit'
import React, { useCallback } from 'react'

const MenuBar = ({ editor }) => {
if (!editor) {
return null
}

const onCutToStart = useCallback(() => {
editor.chain().cut({ from: editor.state.selection.$from.pos, to: editor.state.selection.$to.pos }, 1).run()
}, [editor])
Expand All @@ -20,6 +16,10 @@ const MenuBar = ({ editor }) => {
editor.chain().cut({ from: editor.state.selection.$from.pos, to: editor.state.selection.$to.pos }, editor.state.doc.nodeSize - 2).run()
}, [editor])

if (!editor) {
return null
}

return (
<div className="control-group">
<div className="button-group">
Expand Down
8 changes: 5 additions & 3 deletions demos/src/Examples/Performance/React/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function EditorInstance({ shouldOptimizeRendering }) {
})

return (
<>
<div>
<div className="control-group">
<div>Number of renders: <span id="render-count">{countRenderRef.current}</span></div>
</div>
Expand All @@ -89,12 +89,13 @@ function EditorInstance({ shouldOptimizeRendering }) {
</BubbleMenu>
)}
<EditorContent editor={editor} />
</>
</div>
)
}

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

return (
<>
Expand Down Expand Up @@ -123,8 +124,9 @@ const EditorControls = () => {
Render every transaction (default behavior)
</label>
</div>
<button onClick={() => setRendered(a => !a)}>Toggle rendered</button>
</div>
<EditorInstance shouldOptimizeRendering={shouldOptimizeRendering} />
{rendered && <EditorInstance shouldOptimizeRendering={shouldOptimizeRendering} />}
</>
)
}
Expand Down
1 change: 1 addition & 0 deletions demos/src/GuideContent/ReadOnly/React/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React, { useEffect, useState } from 'react'
export default () => {
const [editable, setEditable] = useState(false)
const editor = useEditor({
shouldRerenderOnTransaction: false,
editable,
content: `
<p>
Expand Down
Loading

0 comments on commit 7c8889a

Please sign in to comment.