Skip to content

Commit

Permalink
fix: remove memory leaks in Text and Editable
Browse files Browse the repository at this point in the history
  • Loading branch information
edhager committed Feb 17, 2023
1 parent 8bbda10 commit da5ca08
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 17 deletions.
21 changes: 19 additions & 2 deletions packages/slate-react/src/components/editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export const Editable = (props: EditableProps) => {
const editor = useSlate()
// Rerender editor when composition status changed
const [isComposing, setIsComposing] = useState(false)
const ref = useRef<HTMLDivElement>(null)
const ref = useRef<HTMLDivElement | null>(null)
const deferredOperations = useRef<DeferredOperation[]>([])

const { onUserInput, receivedUserInput } = useTrackUserInput()
Expand Down Expand Up @@ -698,6 +698,23 @@ export const Editable = (props: EditableProps) => {
[readOnly, propsOnDOMBeforeInput]
)

const callbackRef = useCallback(
node => {
if (node == null) {
EDITOR_TO_ELEMENT.delete(editor)
NODE_TO_ELEMENT.delete(editor)

if (HAS_BEFORE_INPUT_SUPPORT) {
// @ts-ignore The `beforeinput` event isn't recognized.
ref.current.removeEventListener('beforeinput', onDOMBeforeInput)
}
}

ref.current = node
},
[ref, onDOMBeforeInput]
)

// Attach a native DOM event handler for `beforeinput` events, because React's
// built-in `onBeforeInput` is actually a leaky polyfill that doesn't expose
// real `beforeinput` events sadly... (2019/11/04)
Expand Down Expand Up @@ -845,7 +862,7 @@ export const Editable = (props: EditableProps) => {
// this magic zIndex="-1" will fix it
zindex={-1}
suppressContentEditableWarning
ref={ref}
ref={callbackRef}
style={{
...(disableDefaultStyles
? {}
Expand Down
36 changes: 21 additions & 15 deletions packages/slate-react/src/components/text.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef } from 'react'
import React, { useRef, useCallback } from 'react'
import { Element, Range, Text as SlateText } from 'slate'
import { ReactEditor, useSlateStatic } from '..'
import { useIsomorphicLayoutEffect } from '../hooks/use-isomorphic-layout-effect'
Expand Down Expand Up @@ -32,7 +32,7 @@ const Text = (props: {
text,
} = props
const editor = useSlateStatic()
const ref = useRef<HTMLSpanElement>(null)
const ref = useRef<HTMLSpanElement | null>(null)
const leaves = SlateText.decorations(text, decorations)
const key = ReactEditor.findKey(editor, text)
const children = []
Expand All @@ -54,20 +54,26 @@ const Text = (props: {
}

// Update element-related weak maps with the DOM element ref.
useIsomorphicLayoutEffect(() => {
const KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor)
if (ref.current) {
KEY_TO_ELEMENT?.set(key, ref.current)
NODE_TO_ELEMENT.set(text, ref.current)
ELEMENT_TO_NODE.set(ref.current, text)
} else {
KEY_TO_ELEMENT?.delete(key)
NODE_TO_ELEMENT.delete(text)
}
})

const callbackRef = useCallback(
(span: HTMLSpanElement | null) => {
const KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor)
if (span) {
KEY_TO_ELEMENT?.set(key, span)
NODE_TO_ELEMENT.set(text, span)
ELEMENT_TO_NODE.set(span, text)
} else {
KEY_TO_ELEMENT?.delete(key)
NODE_TO_ELEMENT.delete(text)
if (ref.current) {
ELEMENT_TO_NODE.delete(ref.current)
}
}
ref.current = span
},
[ref, editor, key, text]
)
return (
<span data-slate-node="text" ref={ref}>
<span data-slate-node="text" ref={callbackRef}>
{children}
</span>
)
Expand Down

0 comments on commit da5ca08

Please sign in to comment.