Socket
Book a DemoInstallSign in
Socket

@tldraw/state

Package Overview
Dependencies
Maintainers
4
Versions
2576
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tldraw/state - npm Package Compare versions

Comparing version

to
3.16.0-canary.e9c30b532b82

13

dist-cjs/index.d.ts

@@ -90,3 +90,3 @@ /* Excluded from this release type: ArraySet */

* @param b - The new value
* @returns
* @returns True if the values are equal, false otherwise.
*/

@@ -205,11 +205,11 @@ isEqual?(a: any, b: any): boolean;

/**
* A method used to compute a diff between the atom's old and new values. If provided, it will not be used unless you also specify {@link AtomOptions.historyLength}.
* A method used to compute a diff between the computed's old and new values. If provided, it will not be used unless you also specify {@link ComputedOptions.historyLength}.
*/
computeDiff?: ComputeDiff<Value, Diff>;
/**
* If provided, this will be used to compare the old and new values of the atom to determine if the value has changed.
* If provided, this will be used to compare the old and new values of the computed to determine if the value has changed.
* By default, values are compared using first using strict equality (`===`), then `Object.is`, and finally any `.equals` method present in the object's prototype chain.
* @param a - The old value
* @param b - The new value
* @returns
* @returns True if the values are equal, false otherwise.
*/

@@ -242,3 +242,2 @@ isEqual?(a: any, b: any): boolean;

export declare interface EffectScheduler<Result> {
/** @internal */
/**

@@ -274,2 +273,3 @@ * Whether this scheduler is attached and actively listening to its parents.

* It will no longer be eligible to receive 'maybeScheduleEffect' calls until `EffectScheduler.attach` is called again.
* @public
*/

@@ -280,2 +280,3 @@ detach(): void;

* @returns The result of the effect.
* @public
*/

@@ -314,3 +315,3 @@ execute(): Result;

* @param execute - A function that will execute the effect.
* @returns
* @returns void
*/

@@ -317,0 +318,0 @@ scheduleEffect?: (execute: () => void) => void;

@@ -62,5 +62,5 @@ "use strict";

"@tldraw/state",
"3.16.0-canary.dfdf6b7de8c2",
"3.16.0-canary.e9c30b532b82",
"cjs"
);
//# sourceMappingURL=index.js.map

@@ -37,2 +37,3 @@ "use strict";

}
/** @internal */
_isActivelyListening = false;

@@ -49,4 +50,7 @@ /**

lastTraversedEpoch = import_constants.GLOBAL_START_EPOCH;
/** @internal */
lastReactedEpoch = import_constants.GLOBAL_START_EPOCH;
/** @internal */
_scheduleCount = 0;
/** @internal */
__debug_ancestor_epochs__ = null;

@@ -67,2 +71,3 @@ /**

parents = [];
/** @internal */
_scheduleEffect;

@@ -109,2 +114,3 @@ /** @internal */

* It will no longer be eligible to receive 'maybeScheduleEffect' calls until `EffectScheduler.attach` is called again.
* @public
*/

@@ -120,2 +126,3 @@ detach() {

* @returns The result of the effect.
* @public
*/

@@ -122,0 +129,0 @@ execute() {

{
"name": "@tldraw/state",
"description": "tldraw infinite canvas SDK (state).",
"version": "3.16.0-canary.dfdf6b7de8c2",
"version": "3.16.0-canary.e9c30b532b82",
"author": {

@@ -59,3 +59,3 @@ "name": "tldraw Inc.",

"dependencies": {
"@tldraw/utils": "3.16.0-canary.dfdf6b7de8c2"
"@tldraw/utils": "3.16.0-canary.e9c30b532b82"
},

@@ -62,0 +62,0 @@ "typedoc": {

# @tldraw/state
A signals library for tldraw. See also the [React bindings](https://github.com/tldraw/tldraw/tree/main/packages/state-react) for this library.
`@tldraw/state` is a powerful and lightweight TypeScript library for managing reactive state using signals. It provides fine-grained reactive primitives that automatically track dependencies and efficiently update only what needs to change.
To learn more about this check out the [Signia library](https://signia.tldraw.dev/docs/using-signals) which provides the foundational concepts.
`@tldraw/state` powers the reactive system at the heart of [tldraw](https://www.tldraw.com), handling everything from canvas updates to collaborative state synchronization. It's designed to work seamlessly with [@tldraw/store](https://github.com/tldraw/tldraw/tree/main/packages/store) and has optional [React bindings](https://github.com/tldraw/tldraw/tree/main/packages/state-react).
## Contribution
## Why @tldraw/state?
- **Fine-grained reactivity** - Only re-runs computations when their actual dependencies change
- **High performance** - Lazy evaluation and efficient dependency tracking
- **Automatic updates** - Derived values and side effects update automatically
- **Time travel** - Built-in history tracking and transactions with rollback support
- **Framework agnostic** - Works with any JavaScript framework or vanilla JS
- **TypeScript first** - Excellent type safety with full TypeScript support
Perfect for building reactive UIs, real-time collaborative apps, and complex state machines where performance and predictability matter.
## Installation
```bash
npm install @tldraw/state
```
## Quick Start
```ts
import { atom, computed, react } from '@tldraw/state'
// Create reactive state
const name = atom('name', 'World')
const count = atom('count', 0)
// Derive values automatically
const greeting = computed('greeting', () => {
return `Hello, ${name.get()}! Count: ${count.get()}`
})
// React to changes
react('logger', () => {
console.log(greeting.get())
})
// Logs: "Hello, World! Count: 0"
// Update state - reactions run automatically
name.set('tldraw')
// Logs: "Hello, tldraw! Count: 0"
count.set(42)
// Logs: "Hello, tldraw! Count: 42"
```
## Core Concepts
### Atoms - State Containers
Atoms hold raw values and are the foundation of your reactive state:
```ts
import { atom } from '@tldraw/state'
// Create atoms with initial values
const user = atom('user', { name: 'Alice', age: 30 })
const theme = atom('theme', 'light')
// Read values
console.log(user.get().name) // 'Alice'
// Update values
user.update((current) => ({ ...current, age: 31 }))
theme.set('dark')
```
### Computed Values - Automatic Derivation
Computed signals derive their values from other signals and update automatically:
```ts
import { computed } from '@tldraw/state'
const firstName = atom('firstName', 'John')
const lastName = atom('lastName', 'Doe')
const fullName = computed('fullName', () => {
return `${firstName.get()} ${lastName.get()}`
})
console.log(fullName.get()) // "John Doe"
firstName.set('Jane')
console.log(fullName.get()) // "Jane Doe" - automatically updated!
```
### Reactions - Side Effects
Reactions run side effects when their dependencies change:
```ts
import { react } from '@tldraw/state'
const selectedId = atom('selectedId', null)
// Update UI when selection changes
const stop = react('update-selection-ui', () => {
const id = selectedId.get()
document.getElementById('selected').textContent = id || 'None'
})
selectedId.set('shape-123')
// UI automatically updates
// Clean up when no longer needed
stop()
```
### Transactions - Batched Updates
Batch multiple updates to prevent intermediate reactions:
```ts
import { transact } from '@tldraw/state'
const x = atom('x', 0)
const y = atom('y', 0)
const position = computed('position', () => `(${x.get()}, ${y.get()})`)
react('log-position', () => console.log(position.get()))
// Logs: "(0, 0)"
transact(() => {
x.set(10)
y.set(20)
// Reaction runs only once after transaction
})
// Logs: "(10, 20)"
```
## Advanced Features
### History & Time Travel
Track changes over time for undo/redo functionality:
```ts
const canvas = atom(
'canvas',
{ shapes: [] },
{
historyLength: 100,
computeDiff: (prev, next) => ({ prev, next }),
}
)
// Make changes...
canvas.update((state) => ({ shapes: [...state.shapes, newShape] }))
// Get diffs since a point in time
const startTime = getGlobalEpoch()
// ... make more changes ...
const diffs = canvas.getDiffSince(startTime)
```
### Performance Optimization
Use `unsafe__withoutCapture` to read values without creating dependencies:
```ts
const expensiveComputed = computed('expensive', () => {
const important = importantValue.get()
// Read this without making it a dependency
const metadata = unsafe__withoutCapture(() => metadataAtom.get())
return computeExpensiveValue(important, metadata)
})
```
### Debugging
Use `whyAmIRunning()` to understand what triggered an update:
```ts
react('debug-reaction', () => {
whyAmIRunning() // Logs dependency tree to console
// Your reaction code...
})
```
## Integration Examples
### With tldraw SDK
```ts
// In a tldraw application
const editor = useEditor()
// Create reactive state that works with tldraw
const selectedShapes = computed('selectedShapes', () => {
return editor.getSelectedShapeIds().map((id) => editor.getShape(id))
})
// React to selection changes
react('update-property-panel', () => {
const shapes = selectedShapes.get()
updatePropertyPanel(shapes)
})
```
### With React
Install the React bindings:
```bash
npm install @tldraw/state-react
```
```tsx
import { useAtom, useComputed } from '@tldraw/state-react'
function Counter() {
const [count, setCount] = useAtom(countAtom)
const doubled = useComputed(() => count * 2, [count])
return (
<div>
<p>Count: {count}</p>
<p>Doubled: {doubled}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
```
## API Reference
For complete API documentation, see [DOCS.md](./DOCS.md).
### Core Functions
- `atom(name, initialValue, options?)` - Create a reactive state container
- `computed(name, computeFn, options?)` - Create a derived value
- `react(name, effectFn, options?)` - Create a side effect
- `transact(fn)` - Batch state updates
### Class-based APIs
- `@computed` - Decorator for computed class properties
### Advanced
- `reactor(name, effectFn)` - Create a controllable reaction
- `unsafe__withoutCapture(fn)` - Read state without creating dependencies
- `whyAmIRunning()` - Debug what triggered an update
- `getComputedInstance(obj, prop)` - Get underlying computed instance
- `getGlobalEpoch()` - Get current time for history tracking
## Related Packages
- **[@tldraw/state-react](../state-react)** - React bindings for @tldraw/state
- **[@tldraw/store](../store)** - Record storage built on @tldraw/state
- **[@tldraw/editor](../editor)** - The tldraw canvas editor
- **[@tldraw/tldraw](../tldraw)** - Complete tldraw UI components
## Examples & Patterns
Looking for more examples? Check out:
- [tldraw SDK examples](https://github.com/tldraw/tldraw/tree/main/apps/examples) - Real-world usage in tldraw applications
## Contributing
Please see our [contributing guide](https://github.com/tldraw/tldraw/blob/main/CONTRIBUTING.md). Found a bug? Please [submit an issue](https://github.com/tldraw/tldraw/issues/new).

@@ -13,3 +276,3 @@

This project is provided under the MIT license found [here](https://github.com/tldraw/tldraw/blob/main/packages/state/LICENSE.md).
This project is licensed under the MIT License found [here](https://github.com/tldraw/tldraw/blob/main/packages/state/LICENSE.md). The tldraw SDK is provided under the [tldraw license](https://github.com/tldraw/tldraw/blob/main/LICENSE.md).

@@ -22,3 +285,3 @@ ## Trademarks

Find us on Twitter/X at [@tldraw](https://twitter.com/tldraw).
Find us on Twitter/X at [@tldraw](https://twitter.com/tldraw). You can contact us by email at [hello@tldraw.com](mailto:hello@tldraw.com).

@@ -25,0 +288,0 @@ ## Community

@@ -33,3 +33,3 @@ import { ArraySet } from './ArraySet'

* @param b - The new value
* @returns
* @returns True if the values are equal, false otherwise.
*/

@@ -36,0 +36,0 @@ isEqual?(a: any, b: any): boolean

@@ -108,11 +108,11 @@ /* eslint-disable prefer-rest-params */

/**
* A method used to compute a diff between the atom's old and new values. If provided, it will not be used unless you also specify {@link AtomOptions.historyLength}.
* A method used to compute a diff between the computed's old and new values. If provided, it will not be used unless you also specify {@link ComputedOptions.historyLength}.
*/
computeDiff?: ComputeDiff<Value, Diff>
/**
* If provided, this will be used to compare the old and new values of the atom to determine if the value has changed.
* If provided, this will be used to compare the old and new values of the computed to determine if the value has changed.
* By default, values are compared using first using strict equality (`===`), then `Object.is`, and finally any `.equals` method present in the object's prototype chain.
* @param a - The old value
* @param b - The new value
* @returns
* @returns True if the values are equal, false otherwise.
*/

@@ -119,0 +119,0 @@ isEqual?(a: any, b: any): boolean

@@ -37,3 +37,3 @@ import { ArraySet } from './ArraySet'

* @param execute - A function that will execute the effect.
* @returns
* @returns void
*/

@@ -45,2 +45,3 @@ // eslint-disable-next-line @typescript-eslint/method-signature-style

class __EffectScheduler__<Result> implements EffectScheduler<Result> {
/** @internal */
private _isActivelyListening = false

@@ -58,4 +59,8 @@ /**

/** @internal */
private lastReactedEpoch = GLOBAL_START_EPOCH
/** @internal */
private _scheduleCount = 0
/** @internal */
__debug_ancestor_epochs__: Map<Signal<any, any>, number> | null = null

@@ -78,2 +83,3 @@

readonly parents: Signal<any, any>[] = []
/** @internal */
private readonly _scheduleEffect?: (execute: () => void) => void

@@ -140,2 +146,3 @@ constructor(

* It will no longer be eligible to receive 'maybeScheduleEffect' calls until `EffectScheduler.attach` is called again.
* @public
*/

@@ -152,2 +159,3 @@ detach() {

* @returns The result of the effect.
* @public
*/

@@ -199,3 +207,2 @@ execute(): Result {

export interface EffectScheduler<Result> {
/** @internal */
/**

@@ -251,2 +258,3 @@ * Whether this scheduler is attached and actively listening to its parents.

* It will no longer be eligible to receive 'maybeScheduleEffect' calls until `EffectScheduler.attach` is called again.
* @public
*/

@@ -258,2 +266,3 @@ detach(): void

* @returns The result of the effect.
* @public
*/

@@ -260,0 +269,0 @@ execute(): Result

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.