diff --git a/packages/editor/src/lib/TldrawEditor.tsx b/packages/editor/src/lib/TldrawEditor.tsx
index aaa6ad7e3..910e1c8af 100644
--- a/packages/editor/src/lib/TldrawEditor.tsx
+++ b/packages/editor/src/lib/TldrawEditor.tsx
@@ -361,44 +361,60 @@ function TldrawEditorWithReadyStore({
setRenderEditor(editor)
}
- const [initialAutoFocus] = useState(autoFocus)
-
- useLayoutEffect(() => {
- const editor = new Editor({
- store,
- shapeUtils,
- bindingUtils,
- tools,
- getContainer: () => container,
- user,
- initialState,
- autoFocus: initialAutoFocus,
- inferDarkMode,
- cameraOptions,
- assetOptions,
- options,
- })
-
- editorRef.current = editor
- setRenderEditor(editor)
-
- return () => {
- editor.dispose()
- }
- }, [
- container,
- shapeUtils,
- bindingUtils,
- tools,
- store,
- user,
- initialState,
- initialAutoFocus,
+ // props in this ref can be changed without causing the editor to be recreated.
+ const editorOptionsRef = useRef({
+ // for these, it's because they're only used when the editor first mounts:
+ autoFocus,
inferDarkMode,
+ initialState,
+
+ // for these, it's because we keep them up to date in a separate effect:
cameraOptions,
- assetOptions,
- options,
- ])
+ })
+ useLayoutEffect(() => {
+ editorOptionsRef.current = {
+ autoFocus,
+ inferDarkMode,
+ initialState,
+ cameraOptions,
+ }
+ }, [autoFocus, inferDarkMode, initialState, cameraOptions])
+
+ useLayoutEffect(
+ () => {
+ const { autoFocus, inferDarkMode, initialState, cameraOptions } = editorOptionsRef.current
+ const editor = new Editor({
+ store,
+ shapeUtils,
+ bindingUtils,
+ tools,
+ getContainer: () => container,
+ user,
+ initialState,
+ autoFocus,
+ inferDarkMode,
+ cameraOptions,
+ assetOptions,
+ options,
+ })
+
+ editorRef.current = editor
+ setRenderEditor(editor)
+
+ return () => {
+ editor.dispose()
+ }
+ },
+ // if any of these change, we need to recreate the editor.
+ [assetOptions, bindingUtils, container, options, shapeUtils, store, tools, user]
+ )
+
+ // keep the editor up to date with the latest camera options
+ useLayoutEffect(() => {
+ if (editor && cameraOptions) {
+ editor.setCameraOptions(cameraOptions)
+ }
+ }, [editor, cameraOptions])
const crashingError = useSyncExternalStore(
useCallback(
diff --git a/packages/tldraw/src/test/TldrawEditor.test.tsx b/packages/tldraw/src/test/TldrawEditor.test.tsx
index 9e33d468b..bad4652d2 100644
--- a/packages/tldraw/src/test/TldrawEditor.test.tsx
+++ b/packages/tldraw/src/test/TldrawEditor.test.tsx
@@ -226,6 +226,24 @@ describe('', () => {
// but strict mode will cause onMount to be called twice
expect(onMount).toHaveBeenCalledTimes(2)
})
+
+ it('allows updating camera options without re-creating the editor', async () => {
+ const editors: Editor[] = []
+ const onMount = jest.fn((editor: Editor) => {
+ if (!editors.includes(editor)) editors.push(editor)
+ })
+
+ const renderer = await renderTldrawComponent(, {
+ waitForPatterns: false,
+ })
+
+ expect(editors.length).toBe(1)
+ expect(editors[0].getCameraOptions().isLocked).toBe(false)
+
+ renderer.rerender()
+ expect(editors.length).toBe(1)
+ expect(editors[0].getCameraOptions().isLocked).toBe(true)
+ })
})
describe('Custom shapes', () => {