packages/store/README.md
@tldraw/store is a library for creating and managing data. In this library, a "record" is an object that is stored under a typed id.
@tldraw/store is used by tldraw to store its data. It is designed to be used with @tldraw/state.
First create types for your records.
interface Book extends BaseRecord<'book'> {
title: string
author: ID<Author>
numPages: number
}
interface Author extends BaseRecord<'author'> {
name: string
isPseudonym: boolean
}
Then create your RecordType instances.
const Book = createRecordType<Book>('book')
const Author = createRecordType<Author>('author').withDefaultProperties(() => ({
isPseudonym: false,
}))
Then create your RecordStore instance.
const store = new RecordStore<Book | Author>()
Then you can create records, add them to the store, update, and remove them.
const tolkeinId = Author.createId('tolkein')
store.put([
Author.create({
id: tolkeinId,
name: 'J.R.R Tolkein',
}),
])
store.update(tolkeinId, (author) => ({
...author,
name: 'DJJ Tolkz',
isPseudonym: true,
}))
store.remove(tolkeinId)
RecordStoreThe RecordStore class is the main class of the library.
const store = new RecordStore()
put(records: R[]): voidAdd some records to the store. It's an error if they already exist.
const record = Author.create({
name: 'J.R.R Tolkein',
id: Author.createId('tolkein'),
})
store.put([record])
update(id: ID<R>, updater: (record: R) => R): voidUpdate a record. To update multiple records at once, use the update method of the TypedRecordStore class.
const id = Author.createId('tolkein')
store.update(id, (r) => ({ ...r, name: 'Jimmy Tolks' }))
remove(ids: ID<R>[]): voidRemove some records from the store via their ids.
const id = Author.createId('tolkein')
store.remove([id])
get(id: ID<R>): RGet the value of a store record by its id.
const id = Author.createId('tolkein')
const result = store.get(id)
allRecords(): R[]Get an array of all values in the store.
const results = store.allRecords()
clear(): voidRemove all records from the store.
store.clear()
has(id: ID<R>): booleanGet whether the record store has an record stored under the given id.
const id = Author.createId('tolkein')
const result = store.has(id)
serialize(filter?: (record: R) => boolean): RecordStoreSnapshot<R>Opposite of deserialize. Creates a JSON payload from the record store.
const serialized = store.serialize()
const serialized = store.serialize((record) => record.name === 'J.R.R Tolkein')
deserialize(snapshot: RecordStoreSnapshot<R>): voidOpposite of serialize. Replace the store's current records with records as defined by a simple JSON structure into the stores.
const serialized = { ... }
store.deserialize(serialized)
listen(listener: ((entry: HistoryEntry) => void): () => voidAdd a new listener to the store. The store will call the function each time the history changes. Returns a function to remove the listener.
store.listen((entry) => doSomethingWith(entry))
mergeRemoteChanges(fn: () => void): voidMerge changes from a remote source without triggering listeners.
store.mergeRemoteChanges(() => {
store.put(recordsFromRemoteSource)
})
createDerivationCache(name: string, derive: ((record: R) => R | undefined)): DerivationCache<R>Create a new derivation cache.
const derivationCache = createDerivationCache('popular_authors', (record) => {
return record.popularity > 62 ? record : undefined
})
RecordTypeThe RecordType class is used to define the structure of a record.
const recordType = new RecordType('author', () => ({ living: true }))
RecordType instances are most often created with createRecordType.
create(properties: Pick<R, RequiredProperties> & Omit<Partial<R>, RequiredProperties>): RCreate a new record of this type.
const record = recordType.create({ name: 'J.R.R Tolkein' })
clone(record: R): RClone a record of this type.
const record = recordType.create({ name: 'J.R.R Tolkein' })
const clone = recordType.clone(record)
createId(): ID<R>Create an Id for a record of this type.
const id = recordType.createId()
createId(id: string): ID<R>Create a custom Id for a record of this type.
const id = recordType.createId('tolkein')
isInstanceCheck if a value is an instance of this record type.
const record = recordType.create({ name: 'J.R.R Tolkein' })
const result1 = recordType.isInstance(record) // true
const result2 = recordType.isInstance(someOtherRecord) // false
isIdCheck if a value is an id for a record of this type.
const id = recordType.createId('tolkein')
const result1 = recordType.isId(id) // true
const result2 = recordType.isId(someOtherId) // false
withDefaultPropertiesCreate a new record type with default properties.
const youngLivingAuthor = new RecordType('author', () => ({ age: 28, living: true }))
const oldDeadAuthor = recordType.withDefaultProperties({ age: 93, living: false })
RecordStoreQueriesTODO
executeQueryTODO
DerivationCacheThe DerivationCache class is used to create a cache of derived records.
const derivationCache = new DerivationCache('popular_authors', (record) => {
return record.popularity > 62 ? record : undefined
})
createRecordTypeA helper used to create a new RecordType instance with no default properties.
const recordType = createRecordType('author'))
assertIdTypeA helper used to assert that a value is an id for a record of a given type.
const id = recordType.createId('tolkein')
assertIdType(id, recordType)
IDA type used to represent a record's id.
const id: ID<Author> = Author.createId('tolkein')
BaseRecordA BaseRecord is a record that has an id and a type. It is the base type for all records.
type AuthorRecord extends BaseRecord<"author"> {
name: string
age: number
living: boolean
}
RecordsDiffA diff describing the changes to a record.
CollectionDiffA diff describing the changes to a collection.
Please see our contributing guide. Found a bug? Please submit an issue.
This project is licensed under the MIT License found here. The tldraw SDK is provided under the tldraw license.
Copyright (c) 2024-present tldraw Inc. The tldraw name and logo are trademarks of tldraw. Please see our trademark guidelines for info on acceptable usage.
Find us on Twitter/X at @tldraw.
Have questions, comments or feedback? Join our discord. For the latest news and release notes, visit tldraw.dev.