State/store example (#4147)

An example of how to subscribe to values in the store and run side
effects using reactors.

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [ ] `feature`
- [ ] `api`
- [x] `other`

### Test plan

1. Create a shape...
2.

- [ ] Unit tests
- [ ] End to end tests

### Release notes

- Added an example that shows how to use track, useValue and useReactor

---------

Co-authored-by: David Sheldrick <d.j.sheldrick@gmail.com>
This commit is contained in:
Taha 2024-07-12 14:58:09 +01:00 committed by GitHub
parent 1c35ed27f9
commit 85f36639ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 100 additions and 0 deletions

View file

@ -0,0 +1,12 @@
---
title: State and store
component: ./StateStoreExample.tsx
category: basic
priority: 3
keywords: [signia, state, store, side, effects, subscribe, track]
---
Tldraw uses signals to manage its state and store. You can subscribe to
values in the store and run side effects when they change.
---

View file

@ -0,0 +1,88 @@
import { TLComponents, Tldraw, track, useEditor, useReactor, useValue } from 'tldraw'
import 'tldraw/tldraw.css'
// [1]
const InfoPanel = track(() => {
const editor = useEditor()
const tool = editor.getCurrentToolId()
const zoom = editor.getZoomLevel().toFixed(2)
useReactor(
'change title',
() => {
const shapes = editor.getCurrentPageShapes()
document.title = `shapes: ${shapes.length}`
},
[editor]
)
return (
<div style={{ pointerEvents: 'all', backgroundColor: 'thistle', fontSize: 14, padding: 8 }}>
<div>tool: {tool}</div>
<div>zoom: {zoom}</div>
</div>
)
})
// [2]
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function AlternativeInfoPanel() {
const editor = useEditor()
const tool = useValue(
'current tool',
() => {
if (!editor) throw new Error('No editor')
return `Current Tool: ${editor.getCurrentToolId()}`
},
[editor]
)
const zoom = useValue(
'zoom',
() => {
if (!editor) throw new Error('No editor')
return `Zoom Level: ${editor.getZoomLevel().toFixed(2)}`
},
[editor]
)
return (
<div style={{ pointerEvents: 'all', backgroundColor: 'thistle', fontSize: 14, padding: 8 }}>
<div>{tool}</div>
<div>{zoom}</div>
</div>
)
}
const components: TLComponents = {
SharePanel: InfoPanel,
}
export default function StateStoreExample() {
return (
<div className="tldraw__editor">
<Tldraw components={components} />
</div>
)
}
/*
Tldraw uses signals to manage its state and store. You can subscribe to values in the store
and run side effects when they change.
[1]
Our InfoPanel component will display above the style panel. We want it to show the current
selected tool and zoom level of the editor. In order to make sure it displays up-to-date
information, we can wrap the component in the track function. This will track any signals
used in the component and re-render it when they change.
We also use the useReactor hook to update the document title with the number of shapes. This
side effect will run whenever the shapes on the page change. We pass the editor as a
dependency to the useReactor hook so it will always have the latest editor instance.
useQuickReactor runs immediately, whereas useReactor runs on the next animation frame.
Check out the Fog of War example to see useReactor in action.
[2]
We can also use the useValue hook to subscribe to a value in the store. You can pass it a
value or a function. Functions will be memoized and only re-run when the dependencies change.
*/