2d2a7ea76f
React's strict mode runs effects twice on mount, but once it's done that it'll go forward with the state from the first effect. For example, this component: ```tsx let nextId = 1 function Component() { const [state, setState] = useState(null) useEffect(() => { const id = nextId++ console.log('set up', id) setState(id) return () => console.log('tear down', id) }, []) if (!state) return console.log('render', state) } ``` Would log something like this when mounting for the first time: - `set up 1` - `tear down 1` - `set up 2` - `render 1` For us, this is a problem: editor 2 is the version that's still running, but editor 1 is getting used for render. React talks a bit about this issue here: https://github.com/reactwg/react-18/discussions/19 The fix seems to be to keep the editor in a `useRef` instead of a `useState`. We need the state to trigger re-renders though, so we sync the ref into the state although we don't actually use the state value. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `bugfix` — Bug fix ### Release Notes - Fix a bug causing text shape measurement to work incorrectly when using react strict mode
92 lines
2.5 KiB
TypeScript
92 lines
2.5 KiB
TypeScript
import { getAssetUrlsByMetaUrl } from '@tldraw/assets/urls'
|
|
import { createRoot } from 'react-dom/client'
|
|
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
|
|
import {
|
|
DefaultErrorFallback,
|
|
ErrorBoundary,
|
|
setDefaultEditorAssetUrls,
|
|
setDefaultUiAssetUrls,
|
|
} from 'tldraw'
|
|
import { ExamplePage } from './ExamplePage'
|
|
import { examples } from './examples'
|
|
import Develop from './misc/develop'
|
|
import EndToEnd from './misc/end-to-end'
|
|
|
|
// we use secret internal `setDefaultAssetUrls` functions to set these at the
|
|
// top-level so assets don't need to be passed down in every single example.
|
|
const assetUrls = getAssetUrlsByMetaUrl()
|
|
// eslint-disable-next-line local/no-at-internal
|
|
setDefaultEditorAssetUrls(assetUrls)
|
|
// eslint-disable-next-line local/no-at-internal
|
|
setDefaultUiAssetUrls(assetUrls)
|
|
const gettingStartedExamples = examples.find((e) => e.id === 'Getting started')
|
|
if (!gettingStartedExamples) throw new Error('Could not find getting started exmaples')
|
|
const basicExample = gettingStartedExamples.value.find((e) => e.title === 'Tldraw component')
|
|
if (!basicExample) throw new Error('Could not find initial example')
|
|
|
|
const router = createBrowserRouter([
|
|
{
|
|
path: '*',
|
|
lazy: async () => ({ element: <div>404</div> }),
|
|
},
|
|
{
|
|
path: '/',
|
|
lazy: async () => {
|
|
const Component = await basicExample.loadComponent()
|
|
return {
|
|
element: (
|
|
<ExamplePage example={basicExample}>
|
|
<Component />
|
|
</ExamplePage>
|
|
),
|
|
}
|
|
},
|
|
},
|
|
{
|
|
path: 'develop',
|
|
lazy: async () => ({ element: <Develop /> }),
|
|
},
|
|
{
|
|
path: 'end-to-end',
|
|
lazy: async () => ({ element: <EndToEnd /> }),
|
|
},
|
|
...examples.flatMap((exampleArray) =>
|
|
exampleArray.value.flatMap((example) => [
|
|
{
|
|
path: example.path,
|
|
lazy: async () => {
|
|
const Component = await example.loadComponent()
|
|
return {
|
|
element: (
|
|
<ExamplePage example={example}>
|
|
<Component />
|
|
</ExamplePage>
|
|
),
|
|
}
|
|
},
|
|
},
|
|
{
|
|
path: `${example.path}/full`,
|
|
lazy: async () => {
|
|
const Component = await example.loadComponent()
|
|
return {
|
|
element: <Component />,
|
|
}
|
|
},
|
|
},
|
|
])
|
|
),
|
|
])
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const rootElement = document.getElementById('root')!
|
|
const root = createRoot(rootElement!)
|
|
root.render(
|
|
<ErrorBoundary
|
|
fallback={(error) => <DefaultErrorFallback error={error} />}
|
|
onError={(error) => console.error(error)}
|
|
>
|
|
<RouterProvider router={router} />
|
|
</ErrorBoundary>
|
|
)
|
|
})
|