diff --git a/.changeset/text-leaf-memoization.md b/.changeset/text-leaf-memoization.md new file mode 100644 index 000000000..73eb3aa3a --- /dev/null +++ b/.changeset/text-leaf-memoization.md @@ -0,0 +1,5 @@ +--- +'slate-react': patch +--- + +Use memoization to avoid unnecessary `textContent` updates in `` component. diff --git a/packages/slate-react/src/components/string.tsx b/packages/slate-react/src/components/string.tsx index b66de3791..1f7fde588 100644 --- a/packages/slate-react/src/components/string.tsx +++ b/packages/slate-react/src/components/string.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from 'react' +import React, { forwardRef, memo, useRef, useState } from 'react' import { Editor, Text, Path, Element, Node } from 'slate' import { ReactEditor, useSlateStatic } from '..' @@ -61,12 +61,11 @@ const String = (props: { */ const TextString = (props: { text: string; isTrailing?: boolean }) => { const { text, isTrailing = false } = props - const ref = useRef(null) - const getTextContent = () => { return `${text ?? ''}${isTrailing ? '\n' : ''}` } + const [initialText] = useState(getTextContent) // This is the actual text rendering boundary where we interface with the DOM // The text is not rendered as part of the virtual DOM, as since we handle basic character insertions natively, @@ -89,19 +88,20 @@ const TextString = (props: { text: string; isTrailing?: boolean }) => { // as this effectively replaces "specifying the text in the virtual DOM under the below" on each render }) - // Render text content immediately if it's the first-time render - // Ensure that text content is rendered on server-side rendering - if (!ref.current) { + // We intentionally render a memoized that only receives the initial text content when the component is mounted. + // We defer to the layout effect above to update the `textContent` of the span element when needed. + return {initialText} +} + +const MemoizedText = memo( + forwardRef((props, ref) => { return ( - {getTextContent()} + {props.children} ) - } - - // the span is intentionally same on every render in virtual DOM, actual rendering happens in the layout effect above - return -} + }) +) /** * Leaf strings without text, render as zero-width strings.