WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 41601d1

Browse files
authored
fix: resolve invalid position issue in React node views during updates (#7294)
* fix: resolve invalid position issue in React node views during updates * fix: optimize selection updates in React node views with requestAnimationFrame * fix: update selection handling in ReactNodeView to retrieve position correctly
1 parent 5db9bc1 commit 41601d1

File tree

2 files changed

+36
-14
lines changed

2 files changed

+36
-14
lines changed

.changeset/weak-seas-sniff.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tiptap/react': patch
3+
---
4+
5+
Fix a bug where React node views could receive invalid positions from `this.getPos()` when ProseMirror and React render cycles got out of sync, which could cause errors during updates.

packages/react/src/ReactNodeViewRenderer.tsx

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ export class ReactNodeView<
6767
*/
6868
contentDOMElement!: HTMLElement | null
6969

70+
/**
71+
* The requestAnimationFrame ID used for selection updates.
72+
*/
73+
selectionRafId: number | null = null
74+
7075
constructor(component: Component, props: NodeViewRendererProps, options?: Partial<Options>) {
7176
super(component, props, options)
7277

@@ -200,26 +205,33 @@ export class ReactNodeView<
200205
* If it is, call `selectNode`, otherwise call `deselectNode`.
201206
*/
202207
handleSelectionUpdate() {
203-
const { from, to } = this.editor.state.selection
204-
const pos = this.getPos()
205-
206-
if (typeof pos !== 'number') {
207-
return
208+
if (this.selectionRafId) {
209+
cancelAnimationFrame(this.selectionRafId)
210+
this.selectionRafId = null
208211
}
209212

210-
if (from <= pos && to >= pos + this.node.nodeSize) {
211-
if (this.renderer.props.selected) {
213+
this.selectionRafId = requestAnimationFrame(() => {
214+
this.selectionRafId = null
215+
const { from, to } = this.editor.state.selection
216+
const pos = this.getPos()
217+
if (typeof pos !== 'number') {
212218
return
213219
}
214220

215-
this.selectNode()
216-
} else {
217-
if (!this.renderer.props.selected) {
218-
return
219-
}
221+
if (from <= pos && to >= pos + this.node.nodeSize) {
222+
if (this.renderer.props.selected) {
223+
return
224+
}
220225

221-
this.deselectNode()
222-
}
226+
this.selectNode()
227+
} else {
228+
if (!this.renderer.props.selected) {
229+
return
230+
}
231+
232+
this.deselectNode()
233+
}
234+
})
223235
}
224236

225237
/**
@@ -300,6 +312,11 @@ export class ReactNodeView<
300312
this.renderer.destroy()
301313
this.editor.off('selectionUpdate', this.handleSelectionUpdate)
302314
this.contentDOMElement = null
315+
316+
if (this.selectionRafId) {
317+
cancelAnimationFrame(this.selectionRafId)
318+
this.selectionRafId = null
319+
}
303320
}
304321

305322
/**

0 commit comments

Comments
 (0)