mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-12 18:24:03 +02:00
Updating memoization checks on element, leaf, and text components. (#3437)
The element, text, and leaf components do not properly check the decorator list and make sure the properties in addition to the anchor/focus are the same. In addition, the renderLeaf function cannot be memoized because the leaf component does not compare the actual text values within the leafs.
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
NODE_TO_INDEX,
|
||||
KEY_TO_ELEMENT,
|
||||
} from '../utils/weak-maps'
|
||||
import { isDecoratorRangeListEqual } from '../utils/range-list'
|
||||
import { RenderElementProps, RenderLeafProps } from './editable'
|
||||
|
||||
/**
|
||||
@@ -134,7 +135,7 @@ const MemoizedElement = React.memo(Element, (prev, next) => {
|
||||
prev.element === next.element &&
|
||||
prev.renderElement === next.renderElement &&
|
||||
prev.renderLeaf === next.renderLeaf &&
|
||||
isRangeListEqual(prev.decorations, next.decorations) &&
|
||||
isDecoratorRangeListEqual(prev.decorations, next.decorations) &&
|
||||
(prev.selection === next.selection ||
|
||||
(!!prev.selection &&
|
||||
!!next.selection &&
|
||||
@@ -157,29 +158,4 @@ export const DefaultElement = (props: RenderElementProps) => {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a list of ranges is equal to another.
|
||||
*
|
||||
* PERF: this requires the two lists to also have the ranges inside them in the
|
||||
* same order, but this is an okay constraint for us since decorations are
|
||||
* kept in order, and the odd case where they aren't is okay to re-render for.
|
||||
*/
|
||||
|
||||
const isRangeListEqual = (list: Range[], another: Range[]): boolean => {
|
||||
if (list.length !== another.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const range = list[i]
|
||||
const other = another[i]
|
||||
|
||||
if (!Range.equals(range, other)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export default MemoizedElement
|
||||
|
@@ -70,7 +70,9 @@ const MemoizedLeaf = React.memo(Leaf, (prev, next) => {
|
||||
next.isLast === prev.isLast &&
|
||||
next.renderLeaf === prev.renderLeaf &&
|
||||
next.text === prev.text &&
|
||||
Text.matches(next.leaf, prev.leaf)
|
||||
next.leaf.text === prev.leaf.text &&
|
||||
Text.matches(next.leaf, prev.leaf) &&
|
||||
next.leaf[PLACEHOLDER_SYMBOL] === prev.leaf[PLACEHOLDER_SYMBOL]
|
||||
)
|
||||
})
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import {
|
||||
NODE_TO_ELEMENT,
|
||||
ELEMENT_TO_NODE,
|
||||
} from '../utils/weak-maps'
|
||||
import { isDecoratorRangeListEqual } from '../utils/range-list'
|
||||
|
||||
/**
|
||||
* Text.
|
||||
@@ -68,7 +69,8 @@ const MemoizedText = React.memo(Text, (prev, next) => {
|
||||
next.parent === prev.parent &&
|
||||
next.isLast === prev.isLast &&
|
||||
next.renderLeaf === prev.renderLeaf &&
|
||||
next.text === prev.text
|
||||
next.text === prev.text &&
|
||||
isDecoratorRangeListEqual(next.decorations, prev.decorations)
|
||||
)
|
||||
})
|
||||
|
||||
|
43
packages/slate-react/src/utils/range-list.ts
Normal file
43
packages/slate-react/src/utils/range-list.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Range } from 'slate'
|
||||
import { PLACEHOLDER_SYMBOL } from './weak-maps'
|
||||
|
||||
export const shallowCompare = (obj1: {}, obj2: {}) =>
|
||||
Object.keys(obj1).length === Object.keys(obj2).length &&
|
||||
Object.keys(obj1).every(
|
||||
key => obj2.hasOwnProperty(key) && obj1[key] === obj2[key]
|
||||
)
|
||||
|
||||
/**
|
||||
* Check if a list of decorator ranges are equal to another.
|
||||
*
|
||||
* PERF: this requires the two lists to also have the ranges inside them in the
|
||||
* same order, but this is an okay constraint for us since decorations are
|
||||
* kept in order, and the odd case where they aren't is okay to re-render for.
|
||||
*/
|
||||
|
||||
export const isDecoratorRangeListEqual = (
|
||||
list: Range[],
|
||||
another: Range[]
|
||||
): boolean => {
|
||||
if (list.length !== another.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const range = list[i]
|
||||
const other = another[i]
|
||||
|
||||
const { anchor: rangeAnchor, focus: rangeFocus, ...rangeOwnProps } = range
|
||||
const { anchor: otherAnchor, focus: otherFocus, ...otherOwnProps } = other
|
||||
|
||||
if (
|
||||
!Range.equals(range, other) ||
|
||||
range[PLACEHOLDER_SYMBOL] !== other[PLACEHOLDER_SYMBOL] ||
|
||||
!shallowCompare(rangeOwnProps, otherOwnProps)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
Reference in New Issue
Block a user