Skip to content

Commit efb9cd2

Browse files
authored
Merge pull request #3816 from udecode/perf/memo
Perf/memo
2 parents e7127e8 + 3f38c6b commit efb9cd2

File tree

9 files changed

+70
-13
lines changed

9 files changed

+70
-13
lines changed

.changeset/old-olives-scream.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@udecode/plate-core': patch
3+
---
4+
5+
Use useMemoOnce for decorate, usePlateEditor

.changeset/thirty-cats-dress.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@udecode/react-utils': patch
3+
---
4+
5+
Add useEffectOnce, useMemoOnce

apps/www/content/docs/multi-select.mdx

+7-8
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,15 @@ Unlike traditional input-based multi-selects, this component is built on top of
1515

1616
- Full history support (undo/redo)
1717
- Native cursor navigation between and within tags
18-
- Text selection across multiple tags
19-
- Copy/paste support for tags
18+
- Select one to many tags
19+
- Copy/paste tags
2020
- Drag and drop to reorder tags
21-
- Read-only mode support
21+
- Read-only mode
2222
- Duplicate tags prevention
23-
- Support creating new tags if not in the list
24-
- Automatic search text cleanup
25-
- Automatic whitespace trimming
26-
- Headless components for full styling control
27-
- Fuzzy search with keyboard navigation and [cmdk](https://github.com/pacocoursey/cmdk)
23+
- Create new tags, case insensitive
24+
- Search text cleanup
25+
- Whitespace trimming
26+
- Fuzzy search with [cmdk](https://github.com/pacocoursey/cmdk)
2827

2928
</PackageInfo>
3029

packages/core/src/react/components/EditorMethodsEffect.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22

3+
import { useMemoOnce } from '@udecode/react-utils';
4+
35
import {
46
EXPOSED_STORE_KEYS,
57
useEditorRef,
@@ -18,7 +20,7 @@ export const EditorMethodsEffect = ({ id }: { id?: string }) => {
1820
EXPOSED_STORE_KEYS.map((key) => [key, plateStore.set[key]()])
1921
) as any;
2022

21-
const memorizedStoreSetters = React.useMemo(
23+
const memorizedStoreSetters = useMemoOnce(
2224
() => storeSetters,
2325
// eslint-disable-next-line react-hooks/exhaustive-deps
2426
[]

packages/core/src/react/editor/usePlateEditor.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import React from 'react';
1+
import type React from 'react';
22

33
import type { Value } from '@udecode/slate';
44

5+
import { useMemoOnce } from '@udecode/react-utils';
6+
57
import type { AnyPluginConfig } from '../../lib';
68

79
import {
@@ -34,7 +36,7 @@ export function usePlateEditor<
3436
: TEnabled extends true | undefined
3537
? TPlateEditor<V, P>
3638
: TPlateEditor<V, P> | null {
37-
return React.useMemo(
39+
return useMemoOnce(
3840
(): any => {
3941
if (options.enabled === false) return null;
4042

packages/core/src/react/hooks/useEditableProps.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22

33
import type { TEditableProps } from '@udecode/slate-react';
44

5+
import { useMemoOnce } from '@udecode/react-utils';
56
import clsx from 'clsx';
67
import omit from 'lodash/omit.js';
78
import { useDeepCompareMemo } from 'use-deep-compare';
@@ -31,11 +32,11 @@ export const useEditableProps = ({
3132
const storeRenderLeaf = selectors.renderLeaf();
3233
const storeRenderElement = selectors.renderElement();
3334

34-
const decorateMemo = React.useMemo(() => {
35+
const decorateMemo = useMemoOnce(() => {
3536
return pipeDecorate(editor, storeDecorate ?? editableProps?.decorate);
3637
}, [editableProps?.decorate, editor, storeDecorate]);
3738

38-
const decorate: typeof decorateMemo = React.useMemo(() => {
39+
const decorate: typeof decorateMemo = useMemoOnce(() => {
3940
if (!versionDecorate || !decorateMemo) return;
4041

4142
return (entry) => decorateMemo(entry);

packages/react-utils/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ export * from './createPrimitiveComponent';
1111
export * from './createPrimitiveElement';
1212
export * from './createSlotComponent';
1313
export * from './useComposedRef';
14+
export * from './useEffectOnce';
1415
export * from './useIsomorphicLayoutEffect';
16+
export * from './useMemoOnce';
1517
export * from './useOnClickOutside';
1618
export * from './useStableMemo';
1719
export * from './withProviders';
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
3+
export function useEffectOnce(
4+
effect: React.EffectCallback,
5+
deps: React.DependencyList
6+
) {
7+
const initialized = React.useRef(false);
8+
const prevDepsRef = React.useRef(deps);
9+
10+
React.useEffect(() => {
11+
const depsChanged = deps.some((dep, i) => dep !== prevDepsRef.current[i]);
12+
13+
if (!initialized.current || depsChanged) {
14+
initialized.current = true;
15+
prevDepsRef.current = deps;
16+
effect();
17+
}
18+
// eslint-disable-next-line react-hooks/exhaustive-deps
19+
}, deps);
20+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
3+
export function useMemoOnce<T>(
4+
factory: () => T,
5+
deps: React.DependencyList
6+
): T {
7+
const initialized = React.useRef(false);
8+
const prevDepsRef = React.useRef(deps);
9+
const memoizedValueRef = React.useRef<T>();
10+
11+
if (
12+
!initialized.current ||
13+
deps.some((dep, i) => dep !== prevDepsRef.current[i])
14+
) {
15+
initialized.current = true;
16+
prevDepsRef.current = deps;
17+
memoizedValueRef.current = factory();
18+
}
19+
20+
return memoizedValueRef.current!;
21+
}

0 commit comments

Comments
 (0)