Fix editor remounting when camera options change (#4089)
Currently, the editor gets recreated whenever the camera options (or several other props that are only relevant at initialisation time) get changed. This diff makes it so that: - init-only props are kept in a ref so they don't invalidate the editor (but are used when the editor _does_ get recreated) - camera options are kept up to date in a separate effect ### Change type - [x] `bugfix` ### Release notes Fix an issue where changing `cameraOptions` via react would cause the entire editor to re-render
This commit is contained in:
parent
a9ca00c04c
commit
cf32a09221
2 changed files with 70 additions and 36 deletions
|
@ -361,9 +361,28 @@ function TldrawEditorWithReadyStore({
|
|||
setRenderEditor(editor)
|
||||
}
|
||||
|
||||
const [initialAutoFocus] = useState(autoFocus)
|
||||
// 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,
|
||||
})
|
||||
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,
|
||||
|
@ -372,7 +391,7 @@ function TldrawEditorWithReadyStore({
|
|||
getContainer: () => container,
|
||||
user,
|
||||
initialState,
|
||||
autoFocus: initialAutoFocus,
|
||||
autoFocus,
|
||||
inferDarkMode,
|
||||
cameraOptions,
|
||||
assetOptions,
|
||||
|
@ -385,20 +404,17 @@ function TldrawEditorWithReadyStore({
|
|||
return () => {
|
||||
editor.dispose()
|
||||
}
|
||||
}, [
|
||||
container,
|
||||
shapeUtils,
|
||||
bindingUtils,
|
||||
tools,
|
||||
store,
|
||||
user,
|
||||
initialState,
|
||||
initialAutoFocus,
|
||||
inferDarkMode,
|
||||
cameraOptions,
|
||||
assetOptions,
|
||||
options,
|
||||
])
|
||||
},
|
||||
// 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(
|
||||
|
|
|
@ -226,6 +226,24 @@ describe('<TldrawEditor />', () => {
|
|||
// 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(<TldrawEditor onMount={onMount} />, {
|
||||
waitForPatterns: false,
|
||||
})
|
||||
|
||||
expect(editors.length).toBe(1)
|
||||
expect(editors[0].getCameraOptions().isLocked).toBe(false)
|
||||
|
||||
renderer.rerender(<TldrawEditor onMount={onMount} cameraOptions={{ isLocked: true }} />)
|
||||
expect(editors.length).toBe(1)
|
||||
expect(editors[0].getCameraOptions().isLocked).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Custom shapes', () => {
|
||||
|
|
Loading…
Reference in a new issue