mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
fix: decouple react and react-dom imports from utils and make it treeshakeable (#7527)
fix: decouple react and react-dom imports from utils and make it tree-shakeable
This commit is contained in:
parent
8b993d409e
commit
65047cc2cb
7 changed files with 69 additions and 74 deletions
|
@ -22,7 +22,6 @@ import {
|
||||||
preventUnload,
|
preventUnload,
|
||||||
resolvablePromise,
|
resolvablePromise,
|
||||||
throttleRAF,
|
throttleRAF,
|
||||||
withBatchedUpdates,
|
|
||||||
} from "../../packages/excalidraw/utils";
|
} from "../../packages/excalidraw/utils";
|
||||||
import {
|
import {
|
||||||
CURSOR_SYNC_TIMEOUT,
|
CURSOR_SYNC_TIMEOUT,
|
||||||
|
@ -83,6 +82,7 @@ import { atom, useAtom } from "jotai";
|
||||||
import { appJotaiStore } from "../app-jotai";
|
import { appJotaiStore } from "../app-jotai";
|
||||||
import { Mutable, ValueOf } from "../../packages/excalidraw/utility-types";
|
import { Mutable, ValueOf } from "../../packages/excalidraw/utility-types";
|
||||||
import { getVisibleSceneBounds } from "../../packages/excalidraw/element/bounds";
|
import { getVisibleSceneBounds } from "../../packages/excalidraw/element/bounds";
|
||||||
|
import { withBatchedUpdates } from "../../packages/excalidraw/reactUtils";
|
||||||
|
|
||||||
export const collabAPIAtom = atom<CollabAPI | null>(null);
|
export const collabAPIAtom = atom<CollabAPI | null>(null);
|
||||||
export const collabDialogShownAtom = atom(false);
|
export const collabDialogShownAtom = atom(false);
|
||||||
|
|
|
@ -259,9 +259,7 @@ import {
|
||||||
sceneCoordsToViewportCoords,
|
sceneCoordsToViewportCoords,
|
||||||
tupleToCoors,
|
tupleToCoors,
|
||||||
viewportCoordsToSceneCoords,
|
viewportCoordsToSceneCoords,
|
||||||
withBatchedUpdates,
|
|
||||||
wrapEvent,
|
wrapEvent,
|
||||||
withBatchedUpdatesThrottled,
|
|
||||||
updateObject,
|
updateObject,
|
||||||
updateActiveTool,
|
updateActiveTool,
|
||||||
getShortcutKey,
|
getShortcutKey,
|
||||||
|
@ -403,6 +401,7 @@ import { ElementCanvasButton } from "./MagicButton";
|
||||||
import { MagicIcon, copyIcon, fullscreenIcon } from "./icons";
|
import { MagicIcon, copyIcon, fullscreenIcon } from "./icons";
|
||||||
import { EditorLocalStorage } from "../data/EditorLocalStorage";
|
import { EditorLocalStorage } from "../data/EditorLocalStorage";
|
||||||
import FollowMode from "./FollowMode/FollowMode";
|
import FollowMode from "./FollowMode/FollowMode";
|
||||||
|
import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
|
||||||
|
|
||||||
const AppContext = React.createContext<AppClassProperties>(null!);
|
const AppContext = React.createContext<AppClassProperties>(null!);
|
||||||
const AppPropsContext = React.createContext<AppProps>(null!);
|
const AppPropsContext = React.createContext<AppProps>(null!);
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { renderInteractiveScene } from "../../renderer/renderScene";
|
import { renderInteractiveScene } from "../../renderer/renderScene";
|
||||||
import {
|
import { isShallowEqual, sceneCoordsToViewportCoords } from "../../utils";
|
||||||
isRenderThrottlingEnabled,
|
|
||||||
isShallowEqual,
|
|
||||||
sceneCoordsToViewportCoords,
|
|
||||||
} from "../../utils";
|
|
||||||
import { CURSOR_TYPE } from "../../constants";
|
import { CURSOR_TYPE } from "../../constants";
|
||||||
import { t } from "../../i18n";
|
import { t } from "../../i18n";
|
||||||
import type { DOMAttributes } from "react";
|
import type { DOMAttributes } from "react";
|
||||||
|
@ -14,6 +10,7 @@ import type {
|
||||||
RenderInteractiveSceneCallback,
|
RenderInteractiveSceneCallback,
|
||||||
} from "../../scene/types";
|
} from "../../scene/types";
|
||||||
import type { NonDeletedExcalidrawElement } from "../../element/types";
|
import type { NonDeletedExcalidrawElement } from "../../element/types";
|
||||||
|
import { isRenderThrottlingEnabled } from "../../reactUtils";
|
||||||
|
|
||||||
type InteractiveCanvasProps = {
|
type InteractiveCanvasProps = {
|
||||||
containerRef: React.RefObject<HTMLDivElement>;
|
containerRef: React.RefObject<HTMLDivElement>;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||||
import { renderStaticScene } from "../../renderer/renderScene";
|
import { renderStaticScene } from "../../renderer/renderScene";
|
||||||
import { isRenderThrottlingEnabled, isShallowEqual } from "../../utils";
|
import { isShallowEqual } from "../../utils";
|
||||||
import type { AppState, StaticCanvasAppState } from "../../types";
|
import type { AppState, StaticCanvasAppState } from "../../types";
|
||||||
import type { StaticCanvasRenderConfig } from "../../scene/types";
|
import type { StaticCanvasRenderConfig } from "../../scene/types";
|
||||||
import type { NonDeletedExcalidrawElement } from "../../element/types";
|
import type { NonDeletedExcalidrawElement } from "../../element/types";
|
||||||
|
import { isRenderThrottlingEnabled } from "../../reactUtils";
|
||||||
|
|
||||||
type StaticCanvasProps = {
|
type StaticCanvasProps = {
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
|
|
|
@ -5,12 +5,7 @@ import type * as TExcalidraw from "../index";
|
||||||
import "./App.scss";
|
import "./App.scss";
|
||||||
import initialData from "./initialData";
|
import initialData from "./initialData";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import {
|
import { resolvablePromise, ResolvablePromise } from "../utils";
|
||||||
resolvablePromise,
|
|
||||||
ResolvablePromise,
|
|
||||||
withBatchedUpdates,
|
|
||||||
withBatchedUpdatesThrottled,
|
|
||||||
} from "../utils";
|
|
||||||
import { EVENT, ROUNDNESS } from "../constants";
|
import { EVENT, ROUNDNESS } from "../constants";
|
||||||
import { distance2d } from "../math";
|
import { distance2d } from "../math";
|
||||||
import { fileOpen } from "../data/filesystem";
|
import { fileOpen } from "../data/filesystem";
|
||||||
|
@ -29,6 +24,7 @@ import { ImportedLibraryData } from "../data/types";
|
||||||
import CustomFooter from "./CustomFooter";
|
import CustomFooter from "./CustomFooter";
|
||||||
import MobileFooter from "./MobileFooter";
|
import MobileFooter from "./MobileFooter";
|
||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
|
import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
|
61
packages/excalidraw/reactUtils.ts
Normal file
61
packages/excalidraw/reactUtils.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* @param func handler taking at most single parameter (event).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { unstable_batchedUpdates } from "react-dom";
|
||||||
|
import { version as ReactVersion } from "react";
|
||||||
|
import { throttleRAF } from "./utils";
|
||||||
|
|
||||||
|
export const withBatchedUpdates = <
|
||||||
|
TFunction extends ((event: any) => void) | (() => void),
|
||||||
|
>(
|
||||||
|
func: Parameters<TFunction>["length"] extends 0 | 1 ? TFunction : never,
|
||||||
|
) =>
|
||||||
|
((event) => {
|
||||||
|
unstable_batchedUpdates(func as TFunction, event);
|
||||||
|
}) as TFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* barches React state updates and throttles the calls to a single call per
|
||||||
|
* animation frame
|
||||||
|
*/
|
||||||
|
export const withBatchedUpdatesThrottled = <
|
||||||
|
TFunction extends ((event: any) => void) | (() => void),
|
||||||
|
>(
|
||||||
|
func: Parameters<TFunction>["length"] extends 0 | 1 ? TFunction : never,
|
||||||
|
) => {
|
||||||
|
// @ts-ignore
|
||||||
|
return throttleRAF<Parameters<TFunction>>(((event) => {
|
||||||
|
unstable_batchedUpdates(func, event);
|
||||||
|
}) as TFunction);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isRenderThrottlingEnabled = (() => {
|
||||||
|
// we don't want to throttle in react < 18 because of #5439 and it was
|
||||||
|
// getting more complex to maintain the fix
|
||||||
|
let IS_REACT_18_AND_UP: boolean;
|
||||||
|
try {
|
||||||
|
const version = ReactVersion.split(".");
|
||||||
|
IS_REACT_18_AND_UP = Number(version[0]) > 17;
|
||||||
|
} catch {
|
||||||
|
IS_REACT_18_AND_UP = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasWarned = false;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (window.EXCALIDRAW_THROTTLE_RENDER === true) {
|
||||||
|
if (!IS_REACT_18_AND_UP) {
|
||||||
|
if (!hasWarned) {
|
||||||
|
hasWarned = true;
|
||||||
|
console.warn(
|
||||||
|
"Excalidraw: render throttling is disabled on React versions < 18.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
})();
|
|
@ -14,9 +14,7 @@ import {
|
||||||
UnsubscribeCallback,
|
UnsubscribeCallback,
|
||||||
Zoom,
|
Zoom,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { unstable_batchedUpdates } from "react-dom";
|
|
||||||
import { ResolutionType } from "./utility-types";
|
import { ResolutionType } from "./utility-types";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
let mockDateTime: string | null = null;
|
let mockDateTime: string | null = null;
|
||||||
|
|
||||||
|
@ -555,33 +553,6 @@ export const resolvablePromise = <T>() => {
|
||||||
return promise as ResolvablePromise<T>;
|
return promise as ResolvablePromise<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @param func handler taking at most single parameter (event).
|
|
||||||
*/
|
|
||||||
export const withBatchedUpdates = <
|
|
||||||
TFunction extends ((event: any) => void) | (() => void),
|
|
||||||
>(
|
|
||||||
func: Parameters<TFunction>["length"] extends 0 | 1 ? TFunction : never,
|
|
||||||
) =>
|
|
||||||
((event) => {
|
|
||||||
unstable_batchedUpdates(func as TFunction, event);
|
|
||||||
}) as TFunction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* barches React state updates and throttles the calls to a single call per
|
|
||||||
* animation frame
|
|
||||||
*/
|
|
||||||
export const withBatchedUpdatesThrottled = <
|
|
||||||
TFunction extends ((event: any) => void) | (() => void),
|
|
||||||
>(
|
|
||||||
func: Parameters<TFunction>["length"] extends 0 | 1 ? TFunction : never,
|
|
||||||
) => {
|
|
||||||
// @ts-ignore
|
|
||||||
return throttleRAF<Parameters<TFunction>>(((event) => {
|
|
||||||
unstable_batchedUpdates(func, event);
|
|
||||||
}) as TFunction);
|
|
||||||
};
|
|
||||||
|
|
||||||
//https://stackoverflow.com/a/9462382/8418
|
//https://stackoverflow.com/a/9462382/8418
|
||||||
export const nFormatter = (num: number, digits: number): string => {
|
export const nFormatter = (num: number, digits: number): string => {
|
||||||
const si = [
|
const si = [
|
||||||
|
@ -939,36 +910,6 @@ export const memoize = <T extends Record<string, any>, R extends any>(
|
||||||
return ret as typeof func & { clear: () => void };
|
return ret as typeof func & { clear: () => void };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isRenderThrottlingEnabled = (() => {
|
|
||||||
// we don't want to throttle in react < 18 because of #5439 and it was
|
|
||||||
// getting more complex to maintain the fix
|
|
||||||
let IS_REACT_18_AND_UP: boolean;
|
|
||||||
try {
|
|
||||||
const version = React.version.split(".");
|
|
||||||
IS_REACT_18_AND_UP = Number(version[0]) > 17;
|
|
||||||
} catch {
|
|
||||||
IS_REACT_18_AND_UP = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasWarned = false;
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (window.EXCALIDRAW_THROTTLE_RENDER === true) {
|
|
||||||
if (!IS_REACT_18_AND_UP) {
|
|
||||||
if (!hasWarned) {
|
|
||||||
hasWarned = true;
|
|
||||||
console.warn(
|
|
||||||
"Excalidraw: render throttling is disabled on React versions < 18.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
/** Checks if value is inside given collection. Useful for type-safety. */
|
/** Checks if value is inside given collection. Useful for type-safety. */
|
||||||
export const isMemberOf = <T extends string>(
|
export const isMemberOf = <T extends string>(
|
||||||
/** Set/Map/Array/Object */
|
/** Set/Map/Array/Object */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue