diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..d9b922a --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,4 @@ +## 2024-06-07 - Memoize callbacks passed to Excalidraw + +**Learning:** Due to the exceptionally high rendering cost of the `@excalidraw/excalidraw` canvas, always ensure all callbacks passed to it (e.g., `onExcalidrawAPI`) are strictly memoized using `useCallback`. Passing inline functions as props breaks prop stability for `React.memo` and causes severe UI input lag caused by full canvas re-renders when parent states change. +**Action:** When working with `` or its wrapper components like ``, always extract inline callback functions (like `onExcalidrawAPI={(api) => { ... }}`) into `useCallback` hooks before passing them down. diff --git a/src/client/App.tsx b/src/client/App.tsx index 33ddb41..40151b2 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -307,6 +307,13 @@ function PrivateApp() { [], ); + // ⚡ Bolt: Memoize the Excalidraw API callback to prevent EditorCanvas from re-rendering + // Excalidraw has exceptionally high rendering cost, so we must maintain prop stability + // for EditorCanvas's React.memo to avoid severe UI input lag on any parent state change. + const handleExcalidrawAPI = useCallback((api: ExcalidrawImperativeAPI) => { + excalidrawApiRef.current = api; + }, []); + return (
{toastMessage && ( @@ -332,9 +339,7 @@ function PrivateApp() { onSceneChange={handleSceneChange} onSelectionStateChange={handleCodeBlockSelectionChange} onEditorActivity={scheduleThemeTokenSync} - onExcalidrawAPI={(api) => { - excalidrawApiRef.current = api; - }} + onExcalidrawAPI={handleExcalidrawAPI} renderEmbeddable={renderCodeBlockEmbeddable} />