summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks/use-editor.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/hooks/use-editor.ts')
-rw-r--r--apps/web/src/hooks/use-editor.ts63
1 files changed, 41 insertions, 22 deletions
diff --git a/apps/web/src/hooks/use-editor.ts b/apps/web/src/hooks/use-editor.ts
index ff3e2db1..e211a85b 100644
--- a/apps/web/src/hooks/use-editor.ts
+++ b/apps/web/src/hooks/use-editor.ts
@@ -1,39 +1,58 @@
import { useCallback, useMemo, useRef, useSyncExternalStore } from "react";
import { EditorCore } from "@/core";
-export function useEditor(): EditorCore {
+function isShallowEqual(a: unknown, b: unknown): boolean {
+ if (Object.is(a, b)) return true;
+ if (!Array.isArray(a) || !Array.isArray(b)) return false;
+ if (a.length !== b.length) return false;
+ return a.every((item, i) => Object.is(item, b[i]));
+}
+
+const subscribeNone = () => () => {};
+
+export function useEditor(): EditorCore;
+export function useEditor<T>(selector: (editor: EditorCore) => T): T;
+export function useEditor<T>(
+ selector?: (editor: EditorCore) => T,
+): EditorCore | T {
const editor = useMemo(() => EditorCore.getInstance(), []);
- const versionRef = useRef(0);
+ const selectorRef = useRef(selector);
+ selectorRef.current = selector;
- const subscribe = useCallback(
- (onStoreChange: () => void) => {
- const handleStoreChange = () => {
- versionRef.current += 1;
- onStoreChange();
- };
+ const snapshotCacheRef = useRef<unknown>(undefined);
+ const subscribeAll = useCallback(
+ (onChange: () => void) => {
const unsubscribers = [
- editor.playback.subscribe(handleStoreChange),
- editor.timeline.subscribe(handleStoreChange),
- editor.scenes.subscribe(handleStoreChange),
- editor.project.subscribe(handleStoreChange),
- editor.media.subscribe(handleStoreChange),
- editor.renderer.subscribe(handleStoreChange),
- editor.selection.subscribe(handleStoreChange),
+ editor.playback.subscribe(onChange),
+ editor.timeline.subscribe(onChange),
+ editor.scenes.subscribe(onChange),
+ editor.project.subscribe(onChange),
+ editor.media.subscribe(onChange),
+ editor.renderer.subscribe(onChange),
+ editor.selection.subscribe(onChange),
];
-
return () => {
- for (const unsubscribe of unsubscribers) {
+ unsubscribers.forEach((unsubscribe) => {
unsubscribe();
- }
+ });
};
},
[editor],
);
- const getSnapshot = useCallback(() => versionRef.current, []);
-
- useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
+ const getSnapshot = useCallback(() => {
+ const next = selectorRef.current ? selectorRef.current(editor) : editor;
+ if (isShallowEqual(snapshotCacheRef.current, next)) {
+ return snapshotCacheRef.current;
+ }
+ snapshotCacheRef.current = next;
+ return next;
+ }, [editor]);
- return editor;
+ return useSyncExternalStore(
+ selector ? subscribeAll : subscribeNone,
+ getSnapshot,
+ getSnapshot,
+ ) as EditorCore | T;
}