After create/update shape

You can register handlers to run after any record is created or updated. This is most useful for updating other records in response to a particular record changing. In this example, we make sure there's only ever one red shape on a page.

import { Editor, TLShape, TLShapeId, Tldraw, createShapeId } from 'tldraw'

// this function takes a shape ID, and if that shape is red, sets all other red shapes on the same
// page to black.
function ensureOnlyOneRedShape(editor: Editor, shapeId: TLShapeId) {
	// grab the shape and check it's red:
	const shape = editor.getShape(shapeId)!
	if (!isRedShape(shape)) return

	// get the ID of the page that shape belongs to:
	const pageId = editor.getAncestorPageId(shape.id)!

	// find any other red shapes on the same page:
	const otherRedShapesOnPage = Array.from(editor.getPageShapeIds(pageId))
		.map((id) => editor.getShape(id)!)
		.filter((otherShape) => otherShape.id !== shape.id && isRedShape(otherShape))

	// set the color of all those shapes to black:
	editor.updateShapes(
		otherRedShapesOnPage.map((shape) => ({
			id: shape.id,
			type: shape.type,
			props: {
				color: 'black',
			},
		}))
	)
}

function isRedShape(shape: TLShape) {
	return 'color' in shape.props && shape.props.color === 'red'
}

export default function AfterCreateUpdateShapeExample() {
	return (
		<div className="tldraw__editor">
			<Tldraw
				onMount={(editor) => {
					// we can run our `ensureOnlyOneRedShape` function after any shape is created or
					// changed. this means we can enforce our "only one red shape at a time" rule,
					// whilst making sure that the shape most recently set to red is the one that
					// stays red.
					editor.sideEffects.registerAfterCreateHandler('shape', (shape) => {
						ensureOnlyOneRedShape(editor, shape.id)
					})
					editor.sideEffects.registerAfterChangeHandler('shape', (prevShape, nextShape) => {
						ensureOnlyOneRedShape(editor, nextShape.id)
					})

					createDemoShapes(editor)
				}}
			/>
		</div>
	)
}

// create some shapes to demonstrate the side-effects we added
function createDemoShapes(editor: Editor) {
	editor
		.createShapes(
			'there can only be one red shape'.split(' ').map((word, i) => ({
				id: createShapeId(),
				type: 'text',
				y: i * 30,
				props: {
					color: i === 5 ? 'red' : 'black',
					text: word,
				},
			}))
		)
		.zoomToFit({ animation: { duration: 0 } })
}
Prev
Before delete shape
Next
After delete shape