Skip to content

Commit

Permalink
fix(perf): don't sync range decorations while setting up
Browse files Browse the repository at this point in the history
  • Loading branch information
christianhg committed Dec 12, 2024
1 parent 21e0e3e commit af70759
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 8 deletions.
11 changes: 8 additions & 3 deletions packages/editor/src/editor/Editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ export const PortableTextEditable = forwardRef<
// Restore selection from props when the editor has been initialized properly with it's value
useEffect(() => {
const onReady = editorActor.on('ready', () => {
syncRangeDecorations()
restoreSelectionFromProps()
})
const onInvalidValue = editorActor.on('invalid value', () => {
Expand All @@ -380,7 +381,7 @@ export const PortableTextEditable = forwardRef<
onInvalidValue.unsubscribe()
onValueChanged.unsubscribe()
}
}, [editorActor, restoreSelectionFromProps])
}, [editorActor, restoreSelectionFromProps, syncRangeDecorations])

// Restore selection from props when it changes
useEffect(() => {
Expand All @@ -407,9 +408,13 @@ export const PortableTextEditable = forwardRef<

// Sync range decorations after an operation is applied
useEffect(() => {
const teardown = withSyncRangeDecorations(slateEditor, syncRangeDecorations)
const teardown = withSyncRangeDecorations({
editorActor,
slateEditor,
syncRangeDecorations,
})
return () => teardown()
}, [slateEditor, syncRangeDecorations])
}, [editorActor, slateEditor, syncRangeDecorations])

// Handle from props onCopy function
const handleCopy = useCallback(
Expand Down
22 changes: 17 additions & 5 deletions packages/editor/src/editor/withSyncRangeDecorations.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import type {BaseEditor, Operation} from 'slate'
import type {ReactEditor} from 'slate-react'
import type {PortableTextSlateEditor} from '../types/editor'
import type {EditorActor} from './editor-machine'

// React Compiler considers `slateEditor` as immutable, and opts-out if we do this inline in a useEffect, doing it in a function moves it out of the scope, and opts-in again for the rest of the component.
export function withSyncRangeDecorations(
slateEditor: BaseEditor & ReactEditor & PortableTextSlateEditor,
syncRangeDecorations: (operation?: Operation) => void,
) {
export function withSyncRangeDecorations({
editorActor,
slateEditor,
syncRangeDecorations,
}: {
editorActor: EditorActor
slateEditor: BaseEditor & ReactEditor & PortableTextSlateEditor
syncRangeDecorations: (operation?: Operation) => void
}) {
const originalApply = slateEditor.apply

slateEditor.apply = (op: Operation) => {
originalApply(op)
if (op.type !== 'set_selection') {

if (
!editorActor.getSnapshot().matches('setting up') &&
op.type !== 'set_selection'
) {
syncRangeDecorations(op)
}
}

return () => {
slateEditor.apply = originalApply
}
Expand Down

0 comments on commit af70759

Please sign in to comment.