(undefined);
- const [focusIndex, setFocusIndex] = useAtom(
- searchItemInFocusAtom,
- jotaiScope,
- );
+ const [focusIndex, setFocusIndex] = useAtom(searchItemInFocusAtom);
const elementsMap = app.scene.getNonDeletedElementsMap();
useEffect(() => {
diff --git a/packages/excalidraw/components/Sidebar/Sidebar.tsx b/packages/excalidraw/components/Sidebar/Sidebar.tsx
index efa6ccbe3..7c747d2f4 100644
--- a/packages/excalidraw/components/Sidebar/Sidebar.tsx
+++ b/packages/excalidraw/components/Sidebar/Sidebar.tsx
@@ -8,8 +8,7 @@ import React, {
useCallback,
} from "react";
import { Island } from "../Island";
-import { atom, useSetAtom } from "jotai";
-import { jotaiScope } from "../../jotai";
+import { atom, useSetAtom } from "../../editor-jotai";
import type { SidebarProps, SidebarPropsContextValue } from "./common";
import { SidebarPropsContext } from "./common";
import { SidebarHeader } from "./SidebarHeader";
@@ -58,7 +57,7 @@ export const SidebarInner = forwardRef(
const setAppState = useExcalidrawSetAppState();
- const setIsSidebarDockedAtom = useSetAtom(isSidebarDockedAtom, jotaiScope);
+ const setIsSidebarDockedAtom = useSetAtom(isSidebarDockedAtom);
useLayoutEffect(() => {
setIsSidebarDockedAtom(!!docked);
diff --git a/packages/excalidraw/components/TTDDialog/TTDDialog.tsx b/packages/excalidraw/components/TTDDialog/TTDDialog.tsx
index f0c63770a..a683d80ce 100644
--- a/packages/excalidraw/components/TTDDialog/TTDDialog.tsx
+++ b/packages/excalidraw/components/TTDDialog/TTDDialog.tsx
@@ -25,7 +25,7 @@ import type { BinaryFiles } from "../../types";
import { ArrowRightIcon } from "../icons";
import "./TTDDialog.scss";
-import { atom, useAtom } from "jotai";
+import { atom, useAtom } from "../../editor-jotai";
import { trackEvent } from "../../analytics";
import { InlineIcon } from "../InlineIcon";
import { TTDDialogSubmitShortcut } from "./TTDDialogSubmitShortcut";
diff --git a/packages/excalidraw/components/Trans.test.tsx b/packages/excalidraw/components/Trans.test.tsx
index df8ec526e..c331796bd 100644
--- a/packages/excalidraw/components/Trans.test.tsx
+++ b/packages/excalidraw/components/Trans.test.tsx
@@ -4,6 +4,7 @@ import fallbackLangData from "../locales/en.json";
import Trans from "./Trans";
import type { TranslationKeys } from "../i18n";
+import { EditorJotaiProvider } from "../editor-jotai";
describe("Test ", () => {
it("should translate the the strings correctly", () => {
@@ -17,7 +18,7 @@ describe("Test ", () => {
};
const { getByTestId } = render(
- <>
+
", () => {
connect-link={(el) =>
{el}}
/>
- >,
+ ,
);
expect(getByTestId("test1").innerHTML).toEqual("Hello world");
diff --git a/packages/excalidraw/components/hoc/withInternalFallback.tsx b/packages/excalidraw/components/hoc/withInternalFallback.tsx
index 4131c51ec..5906b30f5 100644
--- a/packages/excalidraw/components/hoc/withInternalFallback.tsx
+++ b/packages/excalidraw/components/hoc/withInternalFallback.tsx
@@ -1,6 +1,6 @@
-import { atom, useAtom } from "jotai";
import React, { useLayoutEffect, useRef } from "react";
import { useTunnels } from "../../context/tunnels";
+import { atom } from "../../editor-jotai";
export const withInternalFallback = (
componentName: string,
@@ -13,9 +13,11 @@ export const withInternalFallback =
(
__fallback?: boolean;
}
> = (props) => {
- const { jotaiScope } = useTunnels();
+ const {
+ tunnelsJotai: { useAtom },
+ } = useTunnels();
// for rerenders
- const [, setCounter] = useAtom(renderAtom, jotaiScope);
+ const [, setCounter] = useAtom(renderAtom);
// for initial & subsequent renders. Tracked as component state
// due to excalidraw multi-instance scanerios.
const metaRef = useRef({
diff --git a/packages/excalidraw/components/main-menu/DefaultItems.tsx b/packages/excalidraw/components/main-menu/DefaultItems.tsx
index bb3059db5..632ea4ffa 100644
--- a/packages/excalidraw/components/main-menu/DefaultItems.tsx
+++ b/packages/excalidraw/components/main-menu/DefaultItems.tsx
@@ -32,9 +32,8 @@ import {
actionToggleTheme,
} from "../../actions";
import clsx from "clsx";
-import { useSetAtom } from "jotai";
import { activeConfirmDialogAtom } from "../ActiveConfirmDialog";
-import { jotaiScope } from "../../jotai";
+import { useSetAtom } from "../../editor-jotai";
import { useUIAppState } from "../../context/ui-appState";
import { openConfirmModal } from "../OverwriteConfirm/OverwriteConfirmState";
import Trans from "../Trans";
@@ -189,10 +188,7 @@ Help.displayName = "Help";
export const ClearCanvas = () => {
const { t } = useI18n();
- const setActiveConfirmDialog = useSetAtom(
- activeConfirmDialogAtom,
- jotaiScope,
- );
+ const setActiveConfirmDialog = useSetAtom(activeConfirmDialogAtom);
const actionManager = useExcalidrawActionManager();
if (!actionManager.isActionEnabled(actionClearCanvas)) {
diff --git a/packages/excalidraw/context/tunnels.ts b/packages/excalidraw/context/tunnels.ts
index 8dc325ff9..73b85ba6a 100644
--- a/packages/excalidraw/context/tunnels.ts
+++ b/packages/excalidraw/context/tunnels.ts
@@ -1,5 +1,6 @@
import React from "react";
import tunnel from "tunnel-rat";
+import { createIsolation } from "jotai-scope";
export type Tunnel = ReturnType;
@@ -14,13 +15,17 @@ type TunnelsContextValue = {
DefaultSidebarTabTriggersTunnel: Tunnel;
OverwriteConfirmDialogTunnel: Tunnel;
TTDDialogTriggerTunnel: Tunnel;
- jotaiScope: symbol;
+ // this can be removed once we create jotai stores per each editor
+ // instance
+ tunnelsJotai: ReturnType;
};
export const TunnelsContext = React.createContext(null!);
export const useTunnels = () => React.useContext(TunnelsContext);
+const tunnelsJotai = createIsolation();
+
export const useInitializeTunnels = () => {
return React.useMemo((): TunnelsContextValue => {
return {
@@ -34,7 +39,7 @@ export const useInitializeTunnels = () => {
DefaultSidebarTabTriggersTunnel: tunnel(),
OverwriteConfirmDialogTunnel: tunnel(),
TTDDialogTriggerTunnel: tunnel(),
- jotaiScope: Symbol(),
+ tunnelsJotai,
};
}, []);
};
diff --git a/packages/excalidraw/data/library.ts b/packages/excalidraw/data/library.ts
index 331e7b84e..7eb760c64 100644
--- a/packages/excalidraw/data/library.ts
+++ b/packages/excalidraw/data/library.ts
@@ -8,8 +8,7 @@ import type {
} from "../types";
import { restoreLibraryItems } from "./restore";
import type App from "../components/App";
-import { atom } from "jotai";
-import { jotaiStore } from "../jotai";
+import { atom, editorJotaiStore } from "../editor-jotai";
import type { ExcalidrawElement } from "../element/types";
import { getCommonBoundingBox } from "../element/bounds";
import { AbortError } from "../errors";
@@ -191,13 +190,13 @@ class Library {
private notifyListeners = () => {
if (this.updateQueue.length > 0) {
- jotaiStore.set(libraryItemsAtom, (s) => ({
+ editorJotaiStore.set(libraryItemsAtom, (s) => ({
status: "loading",
libraryItems: this.currLibraryItems,
isInitialized: s.isInitialized,
}));
} else {
- jotaiStore.set(libraryItemsAtom, {
+ editorJotaiStore.set(libraryItemsAtom, {
status: "loaded",
libraryItems: this.currLibraryItems,
isInitialized: true,
@@ -225,7 +224,7 @@ class Library {
destroy = () => {
this.updateQueue = [];
this.currLibraryItems = [];
- jotaiStore.set(libraryItemSvgsCache, new Map());
+ editorJotaiStore.set(libraryItemSvgsCache, new Map());
// TODO uncomment after/if we make jotai store scoped to each excal instance
// jotaiStore.set(libraryItemsAtom, {
// status: "loading",
diff --git a/packages/excalidraw/editor-jotai.ts b/packages/excalidraw/editor-jotai.ts
new file mode 100644
index 000000000..28bc69306
--- /dev/null
+++ b/packages/excalidraw/editor-jotai.ts
@@ -0,0 +1,13 @@
+// eslint-disable-next-line no-restricted-imports
+import { atom, createStore, type PrimitiveAtom } from "jotai";
+import { createIsolation } from "jotai-scope";
+
+const jotai = createIsolation();
+
+export { atom, PrimitiveAtom };
+export const { useAtom, useSetAtom, useAtomValue, useStore } = jotai;
+export const EditorJotaiProvider: ReturnType<
+ typeof createIsolation
+>["Provider"] = jotai.Provider;
+
+export const editorJotaiStore: ReturnType = createStore();
diff --git a/packages/excalidraw/hooks/useLibraryItemSvg.ts b/packages/excalidraw/hooks/useLibraryItemSvg.ts
index b1332cb05..b2ee746e4 100644
--- a/packages/excalidraw/hooks/useLibraryItemSvg.ts
+++ b/packages/excalidraw/hooks/useLibraryItemSvg.ts
@@ -1,7 +1,6 @@
-import { atom, useAtom } from "jotai";
import { useEffect, useState } from "react";
import { COLOR_PALETTE } from "../colors";
-import { jotaiScope } from "../jotai";
+import { atom, useAtom } from "../editor-jotai";
import { exportToSvg } from "../../utils/export";
import type { LibraryItem } from "../types";
@@ -64,7 +63,7 @@ export const useLibraryItemSvg = (
};
export const useLibraryCache = () => {
- const [svgCache] = useAtom(libraryItemSvgsCache, jotaiScope);
+ const [svgCache] = useAtom(libraryItemSvgsCache);
const clearLibraryCache = () => svgCache.clear();
diff --git a/packages/excalidraw/hooks/useScrollPosition.ts b/packages/excalidraw/hooks/useScrollPosition.ts
index e4efb460e..cd913c07d 100644
--- a/packages/excalidraw/hooks/useScrollPosition.ts
+++ b/packages/excalidraw/hooks/useScrollPosition.ts
@@ -1,5 +1,5 @@
import { useEffect } from "react";
-import { atom, useAtom } from "jotai";
+import { atom, useAtom } from "../editor-jotai";
import throttle from "lodash.throttle";
const scrollPositionAtom = atom(0);
diff --git a/packages/excalidraw/i18n.ts b/packages/excalidraw/i18n.ts
index 10373ef56..e1da5fc44 100644
--- a/packages/excalidraw/i18n.ts
+++ b/packages/excalidraw/i18n.ts
@@ -1,7 +1,6 @@
import fallbackLangData from "./locales/en.json";
import percentages from "./locales/percentages.json";
-import { jotaiScope, jotaiStore } from "./jotai";
-import { atom, useAtomValue } from "jotai";
+import { useAtomValue, editorJotaiStore, atom } from "./editor-jotai";
import type { NestedKeyOf } from "./utility-types";
const COMPLETION_THRESHOLD = 85;
@@ -103,7 +102,7 @@ export const setLanguage = async (lang: Language) => {
}
}
- jotaiStore.set(editorLangCodeAtom, lang.code);
+ editorJotaiStore.set(editorLangCodeAtom, lang.code);
};
export const getLanguage = () => currentLang;
@@ -165,6 +164,6 @@ const editorLangCodeAtom = atom(defaultLang.code);
// - component is rendered internally by , but the component
// is memoized w/o being updated on `langCode`, `AppState`, or `UIAppState`
export const useI18n = () => {
- const langCode = useAtomValue(editorLangCodeAtom, jotaiScope);
+ const langCode = useAtomValue(editorLangCodeAtom);
return { t, langCode };
};
diff --git a/packages/excalidraw/index.tsx b/packages/excalidraw/index.tsx
index c83bd7c16..0af660f19 100644
--- a/packages/excalidraw/index.tsx
+++ b/packages/excalidraw/index.tsx
@@ -11,8 +11,7 @@ import "./fonts/fonts.css";
import type { AppProps, ExcalidrawProps } from "./types";
import { defaultLang } from "./i18n";
import { DEFAULT_UI_OPTIONS } from "./constants";
-import { Provider } from "jotai";
-import { jotaiScope, jotaiStore } from "./jotai";
+import { EditorJotaiProvider, editorJotaiStore } from "./editor-jotai";
import Footer from "./components/footer/FooterCenter";
import MainMenu from "./components/main-menu/MainMenu";
import WelcomeScreen from "./components/welcome-screen/WelcomeScreen";
@@ -108,7 +107,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
}, []);
return (
- jotaiStore} scope={jotaiScope}>
+
{
{children}
-
+
);
};
diff --git a/packages/excalidraw/jotai.ts b/packages/excalidraw/jotai.ts
deleted file mode 100644
index 415188425..000000000
--- a/packages/excalidraw/jotai.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { PrimitiveAtom } from "jotai";
-import { unstable_createStore, useAtom } from "jotai";
-import { useLayoutEffect } from "react";
-
-export const jotaiScope = Symbol();
-export const jotaiStore = unstable_createStore();
-
-export const useAtomWithInitialValue = <
- T extends unknown,
- A extends PrimitiveAtom,
->(
- atom: A,
- initialValue: T | (() => T),
-) => {
- const [value, setValue] = useAtom(atom);
-
- useLayoutEffect(() => {
- if (typeof initialValue === "function") {
- // @ts-ignore
- setValue(initialValue());
- } else {
- setValue(initialValue);
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- return [value, setValue] as const;
-};
diff --git a/packages/excalidraw/package.json b/packages/excalidraw/package.json
index a58bd185a..0cbf636fb 100644
--- a/packages/excalidraw/package.json
+++ b/packages/excalidraw/package.json
@@ -70,7 +70,8 @@
"fractional-indexing": "3.2.0",
"fuzzy": "0.1.3",
"image-blob-reduce": "3.0.1",
- "jotai": "1.13.1",
+ "jotai": "2.11.0",
+ "jotai-scope": "0.7.2",
"lodash.throttle": "4.1.1",
"nanoid": "3.3.3",
"open-color": "1.9.1",
diff --git a/yarn.lock b/yarn.lock
index b97d9a706..7eb706e64 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7339,10 +7339,15 @@ jest-worker@^27.4.5:
merge-stream "^2.0.0"
supports-color "^8.0.0"
-jotai@1.13.1:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.13.1.tgz#20cc46454cbb39096b12fddfa635b873b3668236"
- integrity sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw==
+jotai-scope@0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/jotai-scope/-/jotai-scope-0.7.2.tgz#3e9ec5b743bd9f36b08b32cf5151786049bfcce7"
+ integrity sha512-Gwed97f3dDObrO43++2lRcgOqw4O2sdr4JCjP/7eHK1oPACDJ7xKHGScpJX9XaflU+KBHXF+VhwECnzcaQiShg==
+
+jotai@2.11.0:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/jotai/-/jotai-2.11.0.tgz#923f8351e0b2d721036af892c0ae25625049d120"
+ integrity sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"