TinyBase logoTinyBase

API

store

The store module is the core of the TinyBase project and contains the types, interfaces, and functions to work with Store objects.

The main entry point to this module is the createStore function, which returns a new Store. From there, you can set and get data, register listeners, and use other modules to build an entire app around the state and tabular data within.

Since

v1.0.0

Interfaces

There is one interface, Store, within the store module.

Store

A Store is the main location for keeping both tabular data and keyed values.

Create a Store easily with the createStore function. From there, you can set and get data, add listeners for when the data changes, set schemas, and so on.

A Store has two facets. It can contain keyed Values, and independently, it can contain tabular Tables data. These two facets have similar APIs but can be used entirely independently: you can use only tables, only keyed Values, or both tables and keyed Values - all in a single Store.

Keyed values

The keyed value support is best thought of as a flat JavaScript object. The Store contains a number of Value objects, each with a unique ID, and which is a string, boolean, or number.

{                  // Store
  "value1": "one",   // Value (string)
  "value2": true,    // Value (boolean)
  "value3": 3,       // Value (number)
  ...
}

In its default form, a Store has no sense of a structured schema for the Values. However, you can optionally specify a ValuesSchema for a Store, which then usefully constrains and defaults the Values you can use.

Tabular data

The tabular data exists in a simple hierarchical structure:

  • The Store contains a number of Table objects.
  • Each Table contains a number of Row objects.
  • Each Row contains a number of Cell objects.

A Cell is a string, boolean, or number value.

The members of each level of this hierarchy are identified with a unique Id (which is a string). In other words you can naively think of a Store as a three-level-deep JavaScript object, keyed with strings:

{                     // Store
  "table1": {           // Table
    "row1": {             // Row
      "cell1": "one",       // Cell (string)
      "cell2": true,        // Cell (boolean)
      "cell3": 3,           // Cell (number)
      ...
    },
    ...
  },
  ...
}

Again, by default Store has no sense of a structured schema. As long as they are unique within their own parent, the Id keys can each be any string you want. However, as you can optionally specify a TablesSchema for the tabular data in a Store, which then usefully constrains the Table and Cell Ids (and Cell values) you can use.

Setting and getting data

Every part of the Store can be accessed with getter methods. When you retrieve data from the Store, you are receiving a copy - rather than a reference - of it. This means that manipulating the data in the Store must be performed with the equivalent setter and deleter methods.

To benefit from the reactive behavior of the Store, you can also subscribe to changes on any part of it with 'listeners'. Registering a listener returns a listener Id (that you can use later to remove it with the delListener method), and it will then be called every time there is a change within the part of the hierarchy you're listening to.

This table shows the main ways you can set, get, and listen to, different types of data in a Store:

There are two extra methods to manipulate Row objects. The addRow method is like the setRow method but automatically assigns it a new unique Id. And the setPartialRow method lets you update multiple Cell values in a Row without affecting the others. There is a similar setPartialValues method to do the same for the Values in a Store.

You can listen to attempts to write invalid data to a Value or Cell with the addInvalidValueListener method or addInvalidCellListener method.

The transaction method is used to wrap multiple changes to the Store so that the relevant listeners only fire once.

The setJson method and the getJson method allow you to work with a JSON-encoded representation of the entire Store, which is useful for persisting it.

Finally, the callListener method provides a way for you to manually provoke a listener to be called, even if the underlying data hasn't changed. This is useful when you are using mutator listeners to guarantee that data conforms to programmatic conditions, and those conditions change such that you need to update the Store in bulk.

Read more about setting and changing data in The Basics guides, and about listeners in the Listening to Stores guide.

Creating a schema

You can set a ValuesSchema and a TablesSchema with the setValuesSchema method and setTablesSchema method respectively. A TablesSchema constrains the Table Ids the Store can have, and the types of Cell data in each Table. Each Cell requires its type to be specified, and can also take a default value for when it's not specified.

You can also get a serialization of the schemas out of the Store with the getSchemaJson method, and remove the schemas altogether with the delValuesSchema method and delTablesSchema method.

Read more about schemas in the Using Schemas guide.

Convenience methods

There are a few additional helper methods to make it easier to work with a Store. There are methods for easily checking the existence of a Table, Row, or Cell, and iterators that let you act on the children of a common parent:

Since v4.3.23, you can add listeners for the change of existence of part of a Store. For example, the addHasValueListener method lets you listen for a Value being added or removed.

Finally, the getListenerStats method describes the current state of the Store's listeners for debugging purposes.

Example

This example shows a very simple lifecycle of a Store: from creation, to adding and getting some data, and then registering and removing a listener.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getRow('pets', 'fido'));
// -> {species: 'dog'}

store.setCell('pets', 'fido', 'color', 'brown');
console.log(store.getCell('pets', 'fido', 'color'));
// -> 'brown'

const listenerId = store.addTableListener('pets', () => {
  console.log('changed');
});

store.setCell('pets', 'fido', 'sold', false);
// -> 'changed'

store.delListener(listenerId);
See also
Since

v1.0.0

Methods

These are the methods within the Store interface.

Getter methods

This is the collection of getter methods within the Store interface. There are 29 getter methods in total.

getTables

The getTables method returns a Tables object containing the entire tabular data of the Store.

getTables(): Tables
returnsTables

A Tables object containing the tabular data of the Store.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the Store itself.

Examples

This example retrieves the tabular data in a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}, species: {dog: {price: 5}}}

This example retrieves the Tables of an empty Store, returning an empty object.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTables());
// -> {}
Since

v1.0.0

getTablesJson

The getTablesJson method returns a string serialization of all of the Tables in the Store.

getTablesJson(): string
returnsstring

A string serialization of all of the Tables in the Store.

Examples

This example serializes the contents of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getTablesJson());
// -> '{"pets":{"fido":{"species":"dog"}}}'

This example serializes the contents of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTablesJson());
// -> '{}'
Since

v3.0.0

getTablesSchemaJson

The getTablesSchemaJson method returns a string serialization of the TablesSchema of the Store.

getTablesSchemaJson(): string
returnsstring

A string serialization of the TablesSchema of the Store.

If no TablesSchema has been set on the Store (or if it has been removed with the delTablesSchema method), then it will return the serialization of an empty object, {}.

Examples

This example serializes the TablesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {
    species: {type: 'string'},
    sold: {type: 'boolean'},
  },
});
console.log(store.getTablesSchemaJson());
// -> '{"pets":{"species":{"type":"string"},"sold":{"type":"boolean"}}}'

This example serializes the TablesSchema of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTablesSchemaJson());
// -> '{}'
Since

v3.0.0

hasTables

The hasTables method returns a boolean indicating whether any Table objects exist in the Store.

hasTables(): boolean
returnsboolean

Whether any Tables exist.

Example

This example shows simple existence checks.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.hasTables());
// -> false
store.setTables({pets: {fido: {species: 'dog'}}});
console.log(store.hasTables());
// -> true
Since

v1.0.0

hasTablesSchema

The hasTablesSchema method returns a boolean indicating whether the Store currently has a TablesSchema applied to it.

hasTablesSchema(): boolean
returnsboolean

Whether the Store has a TablesSchema applied to it.

Example

This example sets a TablesSchema and checks that it is present.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {
    price: {type: 'number'},
  },
});
console.log(store.hasTablesSchema());
// -> true

store.delTablesSchema();
console.log(store.hasTablesSchema());
// -> false
Since

v4.1.1

getTableIds

The getTableIds method returns the Ids of every Table in the Store.

getTableIds(): Ids
returnsIds

An array of the Ids of every Table in the Store.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Table Ids in a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
console.log(store.getTableIds());
// -> ['pets', 'species']

This example retrieves the Table Ids of an empty Store, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTableIds());
// -> []
Since

v1.0.0

getTable

The getTable method returns an object containing the entire data of a single Table in the Store.

getTable(tableId: string): Table
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsTable

An object containing the entire data of the Table.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the Store itself.

Examples

This example retrieves the data in a single Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
console.log(store.getTable('pets'));
// -> {fido: {species: 'dog'}}

This example retrieves a Table that does not exist, returning an empty object.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getTable('employees'));
// -> {}
Since

v1.0.0

getTableCellIds

The getTableCellIds method returns the Ids of every Cell used across the whole Table.

getTableCellIds(tableId: string): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsIds

An array of the Ids of every Cell used across the whole Table.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Cell Ids used across a whole Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', legs: 4},
    cujo: {dangerous: true},
  },
});
console.log(store.getTableCellIds('pets'));
// -> ['species', 'color', 'legs', 'dangerous']

This example retrieves the Cell Ids used across a Table that does not exist, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getTableCellIds('species'));
// -> []
Since

v3.3.0

hasTable

The hasTable method returns a boolean indicating whether a given Table exists in the Store.

hasTable(tableId: string): boolean
TypeDescription
tableIdstring

The Id of a possible Table in the Store.

returnsboolean

Whether a Table with that Id exists.

Example

This example shows two simple Table existence checks.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.hasTable('pets'));
// -> true
console.log(store.hasTable('employees'));
// -> false
Since

v1.0.0

hasTableCell

The hasTableCell method returns a boolean indicating whether a given Cell exists anywhere in a Table, not just in a specific Row.

hasTableCell(
  tableId: string,
  cellId: string,
): boolean
TypeDescription
tableIdstring

The Id of a possible Table in the Store.

cellIdstring

The Id of a possible Cell in the Table.

returnsboolean

Whether a Cell with that Id exists anywhere in that Table.

Example

This example shows two simple Cell existence checks.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}, felix: {legs: 4}},
});
console.log(store.hasTableCell('pets', 'species'));
// -> true
console.log(store.hasTableCell('pets', 'legs'));
// -> true
console.log(store.hasTableCell('pets', 'color'));
// -> false
Since

v3.3.0

getRowIds

The getRowIds method returns the Ids of every Row in a given Table.

getRowIds(tableId: string): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsIds

An array of the Ids of every Row in the Table.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Row Ids in a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getRowIds('pets'));
// -> ['fido', 'felix']

This example retrieves the Row Ids of a Table that does not exist, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getRowIds('employees'));
// -> []
Since

v1.0.0

getSortedRowIds

The getSortedRowIds method returns the Ids of every Row in a given Table, sorted according to the values in a specified Cell.

getSortedRowIds(
  tableId: string,
  cellId?: string,
  descending?: boolean,
  offset?: number,
  limit?: number,
): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

cellId?string

The Id of the Cell whose values are used for the sorting, or undefined to sort by the Row Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of Row Ids to skip for pagination purposes, if any.

limit?number

The maximum number of Row Ids to return, or undefined for all.

returnsIds

An array of the sorted Ids of every Row in the Table.

The sorting of the rows is alphanumeric, and you can indicate whether it should be in descending order. The offset and limit parameters are used to paginate results, but default to 0 and undefined to return all available Row Ids if not specified.

Note that every call to this method will perform the sorting afresh - there is no caching of the results - and so you are advised to memoize the results yourself, especially when the Table is large. For a performant approach to tracking the sorted Row Ids when they change, use the addSortedRowIdsListener method.

If the Table does not exist, an empty array is returned.

Examples

This example retrieves sorted Row Ids in a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getSortedRowIds('pets', 'species'));
// -> ['felix', 'fido']

This example retrieves sorted Row Ids in a Table in reverse order.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'wolf'},
  },
});
console.log(store.getSortedRowIds('pets', 'species', true));
// -> ['cujo', 'fido', 'felix']

This example retrieves two pages of Row Ids in a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {price: 6},
    felix: {price: 5},
    mickey: {price: 2},
    tom: {price: 4},
    carnaby: {price: 3},
    lowly: {price: 1},
  },
});
console.log(store.getSortedRowIds('pets', 'price', false, 0, 2));
// -> ['lowly', 'mickey']
console.log(store.getSortedRowIds('pets', 'price', false, 2, 2));
// -> ['carnaby', 'tom']

This example retrieves Row Ids sorted by their own value, since the cellId parameter is undefined.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'wolf'},
  },
});
console.log(store.getSortedRowIds('pets'));
// -> ['cujo', 'felix', 'fido']

This example retrieves the sorted Row Ids of a Table that does not exist, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getSortedRowIds('employees'));
// -> []
Since

v2.0.0

getRow

The getRow method returns an object containing the entire data of a single Row in a given Table.

getRow(
  tableId: string,
  rowId: string,
): Row
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

returnsRow

An object containing the entire data of the Row.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the Store itself.

Examples

This example retrieves the data in a single Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getRow('pets', 'fido'));
// -> {species: 'dog'}

This example retrieves a Row that does not exist, returning an empty object.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getRow('pets', 'felix'));
// -> {}
Since

v1.0.0

getRowCount

The getRowCount method returns the count of the Row objects in a given Table.

getRowCount(tableId: string): number
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsnumber

The number of Row objects in the Table.

While this provides the same result as the length of Ids array returned from the getRowIds method, it is somewhat faster, and useful for efficient pagination.

Examples

This example retrieves the number of Row objects in the Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getRowCount('pets'));
// -> 2

This example retrieves the Row Ids of a Table that does not exist, returning zero.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getRowCount('employees'));
// -> 0
Since

v4.1.0

hasRow

The hasRow method returns a boolean indicating whether a given Row exists in the Store.

hasRow(
  tableId: string,
  rowId: string,
): boolean
TypeDescription
tableIdstring

The Id of a possible Table in the Store.

rowIdstring

The Id of a possible Row in the Table.

returnsboolean

Whether a Row with that Id exists in that Table.

Example

This example shows two simple Row existence checks.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.hasRow('pets', 'fido'));
// -> true
console.log(store.hasRow('pets', 'felix'));
// -> false
Since

v1.0.0

getCellIds

The getCellIds method returns the Ids of every Cell in a given Row in a given Table.

getCellIds(
  tableId: string,
  rowId: string,
): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

returnsIds

An array of the Ids of every Cell in the Row.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Cell Ids in a Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog', color: 'brown'},
  },
});
console.log(store.getCellIds('pets', 'fido'));
// -> ['species', 'color']

This example retrieves the Cell Ids of a Row that does not exist, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getCellIds('pets', 'felix'));
// -> []
Since

v1.0.0

getCell

The getCell method returns the value of a single Cell in a given Row, in a given Table.

getCell(
  tableId: string,
  rowId: string,
  cellId: string,
): CellOrUndefined
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

cellIdstring

The Id of the Cell in the Row.

returnsCellOrUndefined

The value of the Cell, or undefined.

Examples

This example retrieves a single Cell.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
console.log(store.getCell('pets', 'fido', 'species'));
// -> 'dog'

This example retrieves a Cell that does not exist, returning undefined.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getCell('pets', 'fido', 'color'));
// -> undefined
Since

v1.0.0

hasCell

The hasCell method returns a boolean indicating whether a given Cell exists in a given Row in a given Table.

hasCell(
  tableId: string,
  rowId: string,
  cellId: string,
): boolean
TypeDescription
tableIdstring

The Id of a possible Table in the Store.

rowIdstring

The Id of a possible Row in the Table.

cellIdstring

The Id of a possible Cell in the Row.

returnsboolean

Whether a Cell with that Id exists in that Row in that Table.

Example

This example shows two simple Cell existence checks.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.hasCell('pets', 'fido', 'species'));
// -> true
console.log(store.hasCell('pets', 'fido', 'color'));
// -> false
Since

v1.0.0

getValues

The getValues method returns an object containing the entire set of keyed Values in the Store.

getValues(): Values
returnsValues

An object containing the set of keyed Values in the Store.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the Store itself.

Examples

This example retrieves the set of keyed Values in the Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValues());
// -> {open: true, employees: 3}

This example retrieves Values from a Store that has none, returning an empty object.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getValues());
// -> {}
Since

v3.0.0

getValuesJson

The getValuesJson method returns a string serialization of all of the keyed Values in the Store.

getValuesJson(): string
returnsstring

A string serialization of all of the Values in the Store.

Examples

This example serializes the keyed value contents of a Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
console.log(store.getValuesJson());
// -> '{"open":true}'

This example serializes the contents of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getValuesJson());
// -> '{}'
Since

v3.0.0

getValuesSchemaJson

The getValuesSchemaJson method returns a string serialization of the ValuesSchema of the Store.

getValuesSchemaJson(): string
returnsstring

A string serialization of the ValuesSchema of the Store.

If no ValuesSchema has been set on the Store (or if it has been removed with the delValuesSchema method), then it will return the serialization of an empty object, {}.

Examples

This example serializes the ValuesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({
  open: {type: 'boolean', default: false},
});
console.log(store.getValuesSchemaJson());
// -> '{"open":{"type":"boolean","default":false}}'

This example serializes the ValuesSchema of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getValuesSchemaJson());
// -> '{}'
Since

v3.0.0

hasValues

The hasValues method returns a boolean indicating whether any Values exist in the Store.

hasValues(): boolean
returnsboolean

Whether any Values exist.

Example

This example shows simple existence checks.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.hasValues());
// -> false
store.setValues({open: true});
console.log(store.hasValues());
// -> true
Since

v3.0.0

hasValuesSchema

The hasValuesSchema method returns a boolean indicating whether the Store currently has a ValuesSchema applied to it.

hasValuesSchema(): boolean
returnsboolean

Whether the Store has a ValuesSchema applied to it.

Example

This example sets a ValuesSchema and checks that it is present.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({open: {type: 'boolean'}});
console.log(store.hasValuesSchema());
// -> true

store.delValuesSchema();
console.log(store.hasValuesSchema());
// -> false
Since

v4.1.1

getValue

The getValue method returns a single keyed Value in the Store.

getValue(valueId: string): ValueOrUndefined
TypeDescription
valueIdstring

The Id of the Value in the Store.

returnsValueOrUndefined

The Value, or undefined.

Examples

This example retrieves a single Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValue('employees'));
// -> 3

This example retrieves a Value that does not exist, returning undefined.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValue('website'));
// -> undefined
Since

v3.0.0

getValueIds

The getValueIds method returns the Ids of every Value in a Store.

getValueIds(): Ids
returnsIds

An array of the Ids of every Value in the Store.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Value Ids in a Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValueIds());
// -> ['open', 'employees']

This example retrieves the Value Ids of a Store that has had none set, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getValueIds());
// -> []
Since

v3.0.0

hasValue

The hasValue method returns a boolean indicating whether a given Value exists in the Store.

hasValue(valueId: string): boolean
TypeDescription
valueIdstring

The Id of a possible Value in the Store.

returnsboolean

Whether a Value with that Id exists in the Store.

Example

This example shows two simple Value existence checks.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
console.log(store.hasValue('open'));
// -> true
console.log(store.hasValue('employees'));
// -> false
Since

v3.0.0

getContent

The getContent method returns a Tables object and a Values object in an array, representing the entire content of the Store.

getContent(): Content
returnsContent

An array of a Tables object and a Values object.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned objects are not made to the Store itself.

Examples

This example retrieves the content of a Store.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog'}}})
  .setValues({open: true, employees: 3});
console.log(store.getContent());
// -> [{pets: {fido: {species: 'dog'}}}, {open: true, employees: 3}]

This example retrieves the Tables and Values of an empty Store, returning empty objects.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getContent());
// -> [{}, {}]
Since

v4.0.0

getJson

The getJson method returns a string serialization of all the Store content: both the Tables and the keyed Values.

getJson(): string
returnsstring

A string serialization of the Tables and Values in the Store.

From v3.0 onwards, the serialization is of an array with two entries. The first is the Tables object, the second the Values. In previous versions (before the existence of the Values data structure), it was a sole object of Tables.

Examples

This example serializes the tabular and keyed value contents of a Store.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog'}}})
  .setValues({open: true});
console.log(store.getJson());
// -> '[{"pets":{"fido":{"species":"dog"}}},{"open":true}]'

This example serializes the contents of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getJson());
// -> '[{},{}]'
Since

v1.0.0

getSchemaJson

The getSchemaJson method returns a string serialization of both the TablesSchema and ValuesSchema of the Store.

getSchemaJson(): string
returnsstring

A string serialization of the TablesSchema and ValuesSchema of the Store.

From v3.0 onwards, the serialization is of an array with two entries. The first is the TablesSchema object, the second the ValuesSchema. In previous versions (before the existence of the ValuesSchema data structure), it was a sole object of TablesSchema.

Examples

This example serializes the TablesSchema and ValuesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore()
  .setTablesSchema({
    pets: {
      price: {type: 'number'},
    },
  })
  .setValuesSchema({
    open: {type: 'boolean'},
  });
console.log(store.getSchemaJson());
// -> '[{"pets":{"price":{"type":"number"}}},{"open":{"type":"boolean"}}]'

This example serializes the TablesSchema and ValuesSchema of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getSchemaJson());
// -> '[{},{}]'
Since

v1.0.0

Setter methods

This is the collection of setter methods within the Store interface. There are 17 setter methods in total.

setTables

The setTables method takes an object and sets the entire tabular data of the Store.

setTables(tables: Tables): this
TypeDescription
tablesTables

The data of the Store to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Tables type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Tables object is valid, any data that was already present in the Store will be completely overwritten. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the tabular data of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}, species: {dog: {price: 5}}}

This example attempts to set the tabular data of an existing Store with partly invalid, and then completely invalid, Tables objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setTables({pets: {felix: {species: 'cat', bug: []}}});
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

store.setTables({meaning: 42});
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
Since

v1.0.0

setTablesJson

The setTablesJson method takes a string serialization of all of the Tables in the Store and attempts to update them to that.

setTablesJson(tablesJson: string): this
TypeDescription
tablesJsonstring

A string serialization of all of the Tables in the Store.

returnsthis

A reference to the Store.

If the JSON cannot be parsed, this will fail silently. If it can be parsed, it will then be subject to the same validation rules as the setTables method (according to the Tables type, and matching any TablesSchema associated with the Store).

Examples

This example sets the tabular contents of a Store from a serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setTablesJson('{"pets": {"fido": {"species": "dog"}}}');
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example attempts to set the tabular contents of a Store from an invalid serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setTablesJson('{"pets": {"fido": {');
console.log(store.getTables());
// -> {}
Since

v3.0.0

setTablesSchema

The setTablesSchema method lets you specify the TablesSchema of the tabular part of the Store.

setTablesSchema(tablesSchema: TablesSchema): this
TypeDescription
tablesSchemaTablesSchema

The TablesSchema to be set for the Store.

returnsthis

A reference to the Store.

Note that this may result in a change to data in the Store, as defaults are applied or as invalid Table, Row, or Cell objects are removed. These changes will fire any listeners to that data, as expected.

When no longer needed, you can also completely remove an existing TablesSchema with the delTablesSchema method.

Example

This example sets the TablesSchema of a Store after it has been created.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {
    species: {type: 'string'},
    sold: {type: 'boolean', default: false},
  },
});
store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});

console.log(store.getTables());
// -> {pets: {0: {species: 'dog', sold: false}}}
Since

v3.0.0

setTable

The setTable method takes an object and sets the entire data of a single Table in the Store.

setTable(
  tableId: string,
  table: Table,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

tableTable

The data of a single Table to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Table type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Table object is valid, any data that was already present in the Store for that Table will be completely overwritten. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the data of a single Table.

import {createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

This example attempts to set the data of an existing Store with partly invalid, and then completely invalid, Table objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setTable('pets', {felix: {species: 'cat', bug: []}});
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

store.setTable('pets', {meaning: 42});
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
Since

v1.0.0

setRow

The setRow method takes an object and sets the entire data of a single Row in the Store.

setRow(
  tableId: string,
  rowId: string,
  row: Row,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

rowRow

The data of a single Row to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Row type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Row object is valid, any data that was already present in the Store for that Row will be completely overwritten. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the data of a single Row.

import {createStore} from 'tinybase';

const store = createStore().setRow('pets', 'fido', {species: 'dog'});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example attempts to set the data of an existing Store with partly invalid, and then completely invalid, Row objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setRow('pets', 'fido', {color: 'brown', bug: []});
console.log(store.getTables());
// -> {pets: {fido: {color: 'brown'}}}

store.setRow('pets', 'fido', 42);
console.log(store.getTables());
// -> {pets: {fido: {color: 'brown'}}}
Since

v1.0.0

addRow

The addRow method takes an object and creates a new Row in the Store, returning the unique Id assigned to it.

addRow(
  tableId: string,
  row: Row,
  reuseRowIds?: boolean,
): undefined | string
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowRow

The data of a single Row to be added.

reuseRowIds?boolean

Whether Ids should be recycled from previously deleted Row objects, defaulting to true.

returnsundefined | string

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Row type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Row object is valid, a new Row will be created. If the object is completely invalid, no change will be made to the Store and the method will return undefined.

You should not guarantee the form of the unique Id that is generated when a Row is added to the Table. However it is likely to be a string representation of an increasing integer.

The reuseRowIds parameter defaults to true, which means that if you delete a Row and then add another, the Id will be re-used - unless you delete the entire Table, in which case all Row Ids will reset. Otherwise, if you specify reuseRowIds to be false, then the Id will be a monotonically increasing string representation of an increasing integer, regardless of any you may have previously deleted.

Examples

This example adds a single Row.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.addRow('pets', {species: 'dog'}));
// -> '0'
console.log(store.getTables());
// -> {pets: {'0': {species: 'dog'}}}

This example attempts to add Rows to an existing Store with partly invalid, and then completely invalid, Row objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {'0': {species: 'dog'}}});

console.log(store.addRow('pets', {species: 'cat', bug: []}));
// -> '1'
console.log(store.getTables());
// -> {pets: {'0': {species: 'dog'}, '1': {species: 'cat'}}}

console.log(store.addRow('pets', 42));
// -> undefined
console.log(store.getTables());
// -> {pets: {'0': {species: 'dog'}, '1': {species: 'cat'}}}
Since

v1.0.0

setPartialRow

The setPartialRow method takes an object and sets partial data of a single Row in the Store, leaving other Cell values unaffected.

setPartialRow(
  tableId: string,
  rowId: string,
  partialRow: Row,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

partialRowRow

The partial data of a single Row to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Row type, or because, when combined with the current Row data, it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Row object is valid, it will be merged with the data that was already present in the Store. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets some of the data of a single Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
store.setPartialRow('pets', 'fido', {color: 'walnut', visits: 1});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'walnut', visits: 1}}}

This example attempts to set some of the data of an existing Store with partly invalid, and then completely invalid, Row objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setPartialRow('pets', 'fido', {color: 'brown', bug: []});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}

store.setPartialRow('pets', 'fido', 42);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
Since

v1.0.0

setCell

The setCell method sets the value of a single Cell in the Store.

setCell(
  tableId: string,
  rowId: string,
  cellId: string,
  cell: Cell | MapCell,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

cellIdstring

The Id of the Cell in the Row.

cellCell | MapCell

The value of the Cell to be set, or a MapCell function to update it.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

If the Cell value is invalid (either because of its type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

As well as string, number, or boolean Cell types, this method can also take a MapCell function that takes the current Cell value as a parameter and maps it. This is useful if you want to efficiently increment a value without fetching it first, for example.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the value of a single Cell.

import {createStore} from 'tinybase';

const store = createStore().setCell('pets', 'fido', 'species', 'dog');
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example sets the data of a single Cell by mapping the existing value.

import {createStore} from 'tinybase';

const increment = (cell) => cell + 1;
const store = createStore().setTables({pets: {fido: {visits: 1}}});

store.setCell('pets', 'fido', 'visits', increment);
console.log(store.getCell('pets', 'fido', 'visits'));
// -> 2

This example attempts to set the data of an existing Store with an invalid Cell value.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setCell('pets', 'fido', 'bug', []);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}
Since

v1.0.0

setPartialValues

The setPartialValues method takes an object and sets its Values in the Store, but leaving existing Values unaffected.

setPartialValues(partialValues: Values): this
TypeDescription
partialValuesValues

The Values to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Values or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Values type, or because, when combined with the current Values data, it does not match a ValuesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Values object is valid, it will be merged with the data that was already present in the Store. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets some of the keyed value data in a Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
store.setPartialValues({employees: 3});
console.log(store.getValues());
// -> {open: true, employees: 3}

This example attempts to set some of the data of an existing Store with partly invalid, and then completely invalid, Values objects.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});

store.setPartialValues({employees: 3, bug: []});
console.log(store.getValues());
// -> {open: true, employees: 3}

store.setPartialValues(42);
console.log(store.getValues());
// -> {open: true, employees: 3}
Since

v3.0.0

setValues

The setValues method takes an object and sets all the Values in the Store.

setValues(values: Values): this
TypeDescription
valuesValues

The Values object to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Value or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Values type, or because it does not match a ValuesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Values object is valid, any data that was already present in the Store for that Values will be completely overwritten. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the Values of a Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValues());
// -> {open: true, employees: 3}

This example attempts to set the data of an existing Store with partly invalid, and then completely invalid, Values objects.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});

store.setValues({employees: 3, bug: []});
console.log(store.getValues());
// -> {employees: 3}

store.setValues(42);
console.log(store.getValues());
// -> {employees: 3}
Since

v3.0.0

setValuesJson

The setValuesJson method takes a string serialization of all of the Values in the Store and attempts to update them to those values.

setValuesJson(valuesJson: string): this
TypeDescription
valuesJsonstring

A string serialization of all of the Values in the Store.

returnsthis

A reference to the Store.

If the JSON cannot be parsed, this will fail silently. If it can be parsed, it will then be subject to the same validation rules as the setValues method (according to the Values type, and matching any ValuesSchema associated with the Store).

Examples

This example sets the keyed value contents of a Store from a serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setValuesJson('{"open": true}');
console.log(store.getValues());
// -> {open: true}

This example attempts to set the keyed value contents of a Store from an invalid serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setValuesJson('{"open": false');
console.log(store.getValues());
// -> {}
Since

v3.0.0

setValuesSchema

The setValuesSchema method lets you specify the ValuesSchema of the keyed Values part of the Store.

setValuesSchema(valuesSchema: ValuesSchema): this
TypeDescription
valuesSchemaValuesSchema

The ValuesSchema to be set for the Store.

returnsthis

A reference to the Store.

Note that this may result in a change to data in the Store, as defaults are applied or as invalid Values are removed. These changes will fire any listeners to that data, as expected.

When no longer needed, you can also completely remove an existing ValuesSchema with the delValuesSchema method.

Example

This example sets the ValuesSchema of a Store after it has been created.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({
  open: {type: 'boolean', default: false},
});
store.setValue('open', 'maybe');

console.log(store.getValues());
// -> {open: false}
Since

v3.0.0

setValue

The setValue method sets a single keyed Value in the Store.

setValue(
  valueId: string,
  value: Value | MapValue,
): this
TypeDescription
valueIdstring

The Id of the Value in the Store.

valueValue | MapValue

The Value to be set, or a MapValue function to update it.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Value, or Id changes resulting from it.

If the Value is invalid (either because of its type, or because it does not match a ValuesSchema associated with the Store), will be ignored silently.

As well as string, number, or boolean Value types, this method can also take a MapValue function that takes the current Value as a parameter and maps it. This is useful if you want to efficiently increment a value without fetching it first, for example.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets a single Value.

import {createStore} from 'tinybase';

const store = createStore().setValue('open', true);
console.log(store.getValues());
// -> {open: true}

This example sets the data of a single Value by mapping the existing Value.

import {createStore} from 'tinybase';

const increment = (value) => value + 1;
const store = createStore().setValues({employees: 3});

store.setValue('employees', increment);
console.log(store.getValue('employees'));
// -> 4

This example attempts to set an invalid Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({employees: 3});

store.setValue('bug', []);
console.log(store.getValues());
// -> {employees: 3}
Since

v3.0.0

applyChanges

The applyChanges method applies a set of Changes to the Store.

applyChanges(changes: Changes): this
TypeDescription
changesChanges

The Changes to apply to the Store.

returnsthis

A reference to the Store.

This method will take a Changes object (which is available at the end of a transaction) and apply it to a Store. The most likely need to do this is to take the changes made during the transaction of one Store, and apply it to the content of another Store - such as when persisting and synchronizing data.

Any part of the provided Changes object are invalid (either because of its type, or because it does not match the schemas associated with the Store) will be ignored silently.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Prior to v5.0, this method was named setTransactionChanges.

Example

This example applies a Changes object that sets a Cell and removes a Value.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store.applyChanges([{pets: {fido: {color: 'black'}}}, {open: null}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'black'}}}
console.log(store.getValues());
// -> {}
Since

v5.0.0

setContent

The setContent method takes an array of two objects and sets the entire data of the Store.

setContent(content: Content): this
TypeDescription
contentContent

An array containing the tabular and keyed-value data of the Store to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, Value, or Id changes resulting from it.

Any part of the provided objects that are invalid (either according to the Tables or Values type, or because it does not match a TablesSchema or ValuesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Tables object or Values object is valid, any data that was already present in that part of the Store will be completely overwritten. If either object is completely invalid, no change will be made to the corresponding part of the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the data of a Store.

import {createStore} from 'tinybase';

const store = createStore().setContent([
  {pets: {fido: {species: 'dog'}}},
  {open: true, employees: 3},
]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store.getValues());
// -> {open: true, employees: 3}

This example attempts to set the data of an existing Store with partly invalid, and then completely invalid objects.

import {createStore} from 'tinybase';

const store = createStore().setContent([
  {pets: {fido: {species: 'dog'}}},
  {open: true, employees: 3},
]);

store.setContent([{pets: {felix: {species: 'cat', bug: []}}}, '']);
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
console.log(store.getValues());
// -> {open: true, employees: 3}

store.setContent([{meaning: 42}]);
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
Since

v4.0.0

setJson

The setJson method takes a string serialization of all of the Tables and Values in the Store and attempts to update them to those values.

setJson(tablesAndValuesJson: string): this
TypeDescription
tablesAndValuesJsonstring

A string serialization of all of the Tables and Values in the Store.

returnsthis

A reference to the Store.

From v3.0 onwards, the serialization should be of an array with two entries. The first is the Tables object, the second the Values. In previous versions (before the existence of the Values data structure), it was a sole object of Tables. For backwards compatibility, if a serialization of a single object is provided, it will be treated as the Tables type.

If the JSON cannot be parsed, this will fail silently. If it can be parsed, it will then be subject to the same validation rules as the setTables method (according to the Tables type, and matching any TablesSchema associated with the Store), and the setValues method (according to the Values type, and matching any ValuesSchema associated with the Store).

Examples

This example sets the tabular and keyed value contents of a Store from a serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setJson('[{"pets": {"fido": {"species": "dog"}}}, {"open": true}]');
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store.getValues());
// -> {open: true}

This example sets the tabular contents of a Store from a legacy single-object serialization (compatible with v2.x and earlier).

import {createStore} from 'tinybase';

const store = createStore();
store.setJson('{"pets": {"fido": {"species": "dog"}}}');
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store.getValues());
// -> {}

This example attempts to set both the tabular and keyed value contents of a Store from an invalid serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setValuesJson('[{"pets": {"fido": {"species": "do');
console.log(store.getTables());
// -> {}
console.log(store.getValues());
// -> {}
Since

v1.0.0

setSchema

The setSchema method lets you specify the TablesSchema and ValuesSchema of the Store.

setSchema(
  tablesSchema: TablesSchema,
  valuesSchema?: ValuesSchema,
): this
TypeDescription
tablesSchemaTablesSchema

The TablesSchema to be set for the Store.

valuesSchema?ValuesSchema

The ValuesSchema to be set for the Store.

returnsthis

A reference to the Store.

Note that this may result in a change to data in the Store, as defaults are applied or as invalid Table, Row, Cell, or Value objects are removed. These changes will fire any listeners to that data, as expected.

From v3.0 onwards, this method takes two arguments. The first is the TablesSchema object, the second the ValuesSchema. In previous versions (before the existence of the ValuesSchema data structure), only the first was present. For backwards compatibility the new second parameter is optional.

Examples

This example sets the TablesSchema and ValuesSchema of a Store after it has been created.

import {createStore} from 'tinybase';

const store = createStore().setSchema(
  {
    pets: {
      species: {type: 'string'},
      sold: {type: 'boolean', default: false},
    },
  },
  {open: {type: 'boolean', default: false}},
);
store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});
store.setValue('open', 'maybe');

console.log(store.getTables());
// -> {pets: {0: {species: 'dog', sold: false}}}
console.log(store.getValues());
// -> {open: false}

This example sets just the TablesSchema of a Store after it has been created.

import {createStore} from 'tinybase';

const store = createStore().setSchema({
  pets: {
    species: {type: 'string'},
    sold: {type: 'boolean', default: false},
  },
});
store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});

console.log(store.getTables());
// -> {pets: {0: {species: 'dog', sold: false}}}
Since

v1.0.0

Listener methods

This is the collection of listener methods within the Store interface. There are 27 listener methods in total.

addHasTablesListener

The addHasTablesListener method registers a listener function with the Store that will be called when Tables as a whole are added to or removed from the Store.

addHasTablesListener(
  listener: HasTablesListener<Store>,
  mutator?: boolean,
): string
TypeDescription
listenerHasTablesListener<Store>

The function that will be called whenever Tables as a whole are added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasTablesListener function, and will be called with a reference to the Store. It is also given a flag to indicate whether Tables now exist (having not done previously), or do not (having done so previously).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to Tables being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTablesListener((store, hasTables) => {
  console.log('Tables ' + (hasTables ? 'added' : 'removed'));
});

store.delTables();
// -> 'Tables removed'

store.setTables({species: {dog: {price: 5}}});
// -> 'Tables added'

store.delListener(listenerId);

This example registers a listener that responds to Tables being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore();
const listenerId = store.addHasTablesListener(
  (store, hasTables) => store.setValue('hasTables', hasTables),
  true,
);

store.setTables({species: {dog: {price: 5}}});
console.log(store.getValues());
// -> {hasTables: true}

store.delListener(listenerId);
Since

v4.4.0

addTablesListener

The addTablesListener method registers a listener function with the Store that will be called whenever data in the Store changes.

addTablesListener(
  listener: TablesListener<Store>,
  mutator?: boolean,
): string
TypeDescription
listenerTablesListener<Store>

The function that will be called whenever data in the Store changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a TablesListener function, and will be called with a reference to the Store and a GetCellChange function in case you need to inspect any changes that occurred.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to the whole Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTablesListener((store, getCellChange) => {
  console.log('Tables changed');
  console.log(getCellChange('pets', 'fido', 'color'));
});

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Tables changed'
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to the whole Store, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTablesListener(
  (store) => store.setCell('meta', 'update', 'store', true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {store: true}}

store.delListener(listenerId);
Since

v1.0.0

addTableIdsListener

The addTableIdsListener method registers a listener function with the Store that will be called whenever the Table Ids in the Store change.

addTableIdsListener(
  listener: TableIdsListener<Store>,
  mutator?: boolean,
): string
TypeDescription
listenerTableIdsListener<Store>

The function that will be called whenever the Table Ids in the Store change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a TableIdsListener function, and will be called with a reference to the Store.

By default, such a listener is only called when a Table is added or removed. To listen to all changes in the Store, use the addTablesListener method.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Table Ids.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addTableIdsListener((store) => {
  console.log('Table Ids changed');
  console.log(store.getTableIds());
});

store.setTable('species', {dog: {price: 5}});
// -> 'Table Ids changed'
// -> ['pets', 'species']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Table Ids, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addTableIdsListener(
  (store) => store.setCell('meta', 'update', 'store', true),
  true, // mutator
);

store.setTable('species', {dog: {price: 5}});
console.log(store.getTable('meta'));
// -> {update: {store: true}}

store.delListener(listenerId);
Since

v1.0.0

addHasTableCellListener

The addHasTableCellListener method registers a listener function with the Store that will be called when a Cell is added to or removed from anywhere in a Table as a whole.

addHasTableCellListener(
  tableId: IdOrNull,
  cellId: IdOrNull,
  listener: HasTableCellListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerHasTableCellListener<Store>

The function that will be called whenever the matching Cell is added to or removed from anywhere in the Table.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasTableCellListener function, and will be called with a reference to the Store, the Id of the Table that changed, and the Id of the Table Cell that changed. It is also given a flag to indicate whether the Cell now exists anywhere in the Table (having not done previously), or does not (having done so previously).

You can either listen to a single Table Cell being added or removed (by specifying the Table Id and Cell Id, as the method's first two parameters) or changes to any Table Cell (by providing null wildcards).

Both, either, or neither of the tableId and cellId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Cell being added to or removed from the Table as a whole.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableCellListener(
  'pets',
  'color',
  (store, tableId, cellId, hasTableCell) => {
    console.log(
      'color cell in pets table ' + (hasTableCell ? 'added' : 'removed'),
    );
  },
);

store.delCell('pets', 'fido', 'color');
// -> 'color cell in pets table removed'

store.setRow('pets', 'felix', {species: 'cat', color: 'brown'});
// -> 'color cell in pets table added'

store.delListener(listenerId);

This example registers a listener that responds to any Cell being added to or removed from the Table as a whole.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableCellListener(
  null,
  null,
  (store, tableId, cellId, hasTableCell) => {
    console.log(
      `${cellId} cell in ${tableId} table ` +
        (hasTableCell ? 'added' : 'removed'),
    );
  },
);

store.delCell('pets', 'fido', 'color');
// -> 'color cell in pets table removed'
store.setTable('species', {dog: {price: 5}});
// -> 'price cell in species table added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Cell being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableCellListener(
  'pets',
  'color',
  (store, tableId, cellId) =>
    store.setCell('meta', 'update', `${tableId}_${cellId}`, true),
  true,
);

store.delRow('pets', 'fido');
console.log(store.getTable('meta'));
// -> {update: {pets_color: true}}

store.delListener(listenerId);
Since

v4.4.0

addHasTableListener

The addHasTableListener method registers a listener function with the Store that will be called when a Table is added to or removed from the Store.

addHasTableListener(
  tableId: IdOrNull,
  listener: HasTableListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerHasTableListener<Store>

The function that will be called whenever the matching Table is added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasTableListener function, and will be called with a reference to the Store and the Id of the Table that changed. It is also given a flag to indicate whether the Table now exists (having not done previously), or does not (having done so previously).

You can either listen to a single Table being added or removed (by specifying the Table Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Table being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableListener(
  'pets',
  (store, tableId, hasTable) => {
    console.log('pets table ' + (hasTable ? 'added' : 'removed'));
  },
);

store.delTable('pets');
// -> 'pets table removed'

store.setTable('pets', {fido: {species: 'dog', color: 'brown'}});
// -> 'pets table added'

store.delListener(listenerId);

This example registers a listener that responds to any Table being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableListener(
  null,
  (store, tableId, hasTable) => {
    console.log(`${tableId} table ` + (hasTable ? 'added' : 'removed'));
  },
);

store.delTable('pets');
// -> 'pets table removed'
store.setTable('species', {dog: {price: 5}});
// -> 'species table added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Table being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableListener(
  'pets',
  (store, tableId) => store.setCell('meta', 'update', tableId, true),
  true,
);

store.delTable('pets', 'fido');
console.log(store.getTable('meta'));
// -> {update: {pets: true}}

store.delListener(listenerId);
Since

v4.4.0

addTableCellIdsListener

The addTableCellIdsListener method registers a listener function with the Store that will be called whenever the Cell Ids that appear anywhere in a Table change.

addTableCellIdsListener(
  tableId: IdOrNull,
  listener: TableCellIdsListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerTableCellIdsListener<Store>

The function that will be called whenever the Cell Ids that appear anywhere in a Table change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a TableCellIdsListener function, and will be called with a reference to the Store and the Id of the Table that changed.

By default, such a listener is only called when a Cell Id is added or removed from the whole of the Table. To listen to all changes in the Table, use the addTableListener method.

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Cell Ids that appear anywhere in a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableCellIdsListener('pets', (store) => {
  console.log('Cell Ids in pets table changed');
  console.log(store.getTableCellIds('pets'));
});

store.setRow('pets', 'felix', {species: 'cat', legs: 4});
// -> 'Cell Ids in pets table changed'
// -> ['species', 'color', 'legs']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Cell Ids that appear anywhere in any Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
  species: {dog: {price: 5}},
});
const listenerId = store.addTableCellIdsListener(
  null,
  (store, tableId) => {
    console.log(`Cell Ids in ${tableId} table changed`);
    console.log(store.getTableCellIds(tableId));
  },
);

store.setRow('pets', 'felix', {species: 'cat', legs: 4});
// -> 'Cell Ids in pets table changed'
// -> ['species', 'color', 'legs']

store.setRow('species', 'cat', {price: 4, friendly: true});
// -> 'Cell Ids in species table changed'
// -> ['price', 'friendly']

store.delListener(listenerId);

This example registers a listener that responds to the Cell Ids that appear anywhere in a Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableCellIdsListener(
  'pets',
  (store, tableId) => store.setCell('meta', 'update', tableId, true),
  true, // mutator
);

store.setRow('pets', 'felix', {species: 'cat', legs: 4});
console.log(store.getTable('meta'));
// -> {update: {pets: true}}

store.delListener(listenerId);
Since

v1.0.0

addTableListener

The addTableListener method registers a listener function with the Store that will be called whenever data in a Table changes.

addTableListener(
  tableId: IdOrNull,
  listener: TableListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerTableListener<Store>

The function that will be called whenever data in the matching Table changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a TableListener function, and will be called with a reference to the Store, the Id of the Table that changed, and a GetCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableListener(
  'pets',
  (store, tableId, getCellChange) => {
    console.log('pets table changed');
    console.log(getCellChange('pets', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'pets table changed'
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableListener(null, (store, tableId) => {
  console.log(`${tableId} table changed`);
});

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'pets table changed'
store.setTable('species', {dog: {price: 5}});
// -> 'species table changed'

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableListener(
  'pets',
  (store, tableId) => store.setCell('meta', 'update', tableId, true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {pets: true}}

store.delListener(listenerId);
Since

v1.0.0

addRowIdsListener

The addRowIdsListener method registers a listener function with the Store that will be called whenever the Row Ids in a Table change.

addRowIdsListener(
  tableId: IdOrNull,
  listener: RowIdsListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerRowIdsListener<Store>

The function that will be called whenever the Row Ids in the Table change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a RowIdsListener function, and will be called with a reference to the Store and the Id of the Table that changed.

By default, such a listener is only called when a Row is added or removed. To listen to all changes in the Table, use the addTableListener method.

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Row Ids of a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowIdsListener('pets', (store) => {
  console.log('Row Ids for pets table changed');
  console.log(store.getRowIds('pets'));
});

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Row Ids for pets table changed'
// -> ['fido', 'felix']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Row Ids of any Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowIdsListener(null, (store, tableId) => {
  console.log(`Row Ids for ${tableId} table changed`);
  console.log(store.getRowIds(tableId));
});

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Row Ids for pets table changed'
// -> ['fido', 'felix']
store.setRow('species', 'dog', {price: 5});
// -> 'Row Ids for species table changed'
// -> ['dog']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Row Ids of a specific Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowIdsListener(
  'pets',
  (store, tableId) => store.setCell('meta', 'update', tableId, true),
  true, // mutator
);

store.setRow('pets', 'felix', {species: 'cat'});
console.log(store.getTable('meta'));
// -> {update: {pets: true}}

store.delListener(listenerId);
Since

v1.0.0

addSortedRowIdsListener

The addSortedRowIdsListener method registers a listener function with the Store that will be called whenever sorted (and optionally, paginated) Row Ids in a Table change.

addSortedRowIdsListener(
  tableId: string,
  cellId: undefined | string,
  descending: boolean,
  offset: number,
  limit: undefined | number,
  listener: SortedRowIdsListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdstring

The Id of the Table to listen to.

cellIdundefined | string

The Id of the Cell whose values are used for the sorting, or undefined to sort by the Row Id itself.

descendingboolean

Whether the sorting should be in descending order.

offsetnumber

The number of Row Ids to skip for pagination purposes, if any.

limitundefined | number

The maximum number of Row Ids to return, or undefined for all.

listenerSortedRowIdsListener<Store>

The function that will be called whenever the sorted Row Ids in the Table change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a SortedRowIdsListener function, and will be called with a reference to the Store, the Id of the Table whose Row Ids sorting changed, the Cell Id being used to sort them, whether descending or not, and the offset and limit of the number of Ids returned, for pagination purposes. It also receives the sorted array of Ids itself, so that you can use them in the listener without the additional cost of an explicit call to getSortedRowIds.

Such a listener is called when a Row is added or removed, but also when a value in the specified Cell (somewhere in the Table) has changed enough to change the sorting of the Row Ids.

Unlike most other listeners, you cannot provide wildcards (due to the cost of detecting changes to the sorting). You can only listen to a single specified Table, sorted by a single specified Cell.

The sorting of the rows is alphanumeric, and you can indicate whether it should be in descending order. The offset and limit parameters are used to paginate results, but default to 0 and undefined to return all available Row Ids if not specified.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the sorted Row Ids of a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    cujo: {species: 'wolf'},
    felix: {species: 'cat'},
  },
});
console.log(store.getSortedRowIds('pets', 'species', false));
// -> ['felix', 'cujo']

const listenerId = store.addSortedRowIdsListener(
  'pets',
  'species',
  false,
  0,
  undefined,
  (store, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`Sorted Row Ids for ${tableId} table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);

store.setRow('pets', 'fido', {species: 'dog'});
// -> 'Sorted Row Ids for pets table changed'
// -> ['felix', 'fido', 'cujo']

store.delListener(listenerId);

This example registers a listener that responds to any change to a paginated section of the sorted Row Ids of a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {price: 6},
    felix: {price: 5},
    mickey: {price: 2},
    tom: {price: 4},
    carnaby: {price: 3},
    lowly: {price: 1},
  },
});

const listenerId = store.addSortedRowIdsListener(
  'pets',
  'price',
  false,
  0,
  3,
  (store, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`First three sorted Row Ids for ${tableId} table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);
console.log(store.getSortedRowIds('pets', 'price', false, 0, 3));
// -> ['lowly', 'mickey', 'carnaby']

store.setCell('pets', 'carnaby', 'price', 4.5);
// -> 'First three sorted Row Ids for pets table changed'
// -> ['lowly', 'mickey', 'tom']

store.delListener(listenerId);

This example registers a listener that responds to any change to the sorted Row Ids of a specific Table. The Row Ids are sorted by their own value, since the cellId parameter is explicitly undefined.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getSortedRowIds('pets', undefined, false));
// -> ['felix', 'fido']

const listenerId = store.addSortedRowIdsListener(
  'pets',
  undefined,
  false,
  0,
  undefined,
  (store, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`Sorted Row Ids for ${tableId} table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);

store.setRow('pets', 'cujo', {species: 'wolf'});
// -> 'Sorted Row Ids for pets table changed'
// -> ['cujo', 'felix', 'fido']

store.delListener(listenerId);

This example registers a listener that responds to a change in the sorting of the rows of a specific Table, even though the set of Ids themselves has not changed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getSortedRowIds('pets', 'species', false));
// -> ['felix', 'fido']

const listenerId = store.addSortedRowIdsListener(
  'pets',
  'species',
  false,
  0,
  undefined,
  (store, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`Sorted Row Ids for ${tableId} table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);

store.setCell('pets', 'felix', 'species', 'tiger');
// -> 'Sorted Row Ids for pets table changed'
// -> ['fido', 'felix']

store.delListener(listenerId);

This example registers a listener that responds to any change to the sorted Row Ids of a specific Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    cujo: {species: 'wolf'},
    felix: {species: 'cat'},
  },
});
const listenerId = store.addSortedRowIdsListener(
  'pets',
  'species',
  false,
  0,
  undefined,
  (store, tableId) => store.setCell('meta', 'sorted', tableId, true),
  true, // mutator
);

store.setRow('pets', 'fido', {species: 'dog'});
console.log(store.getTable('meta'));
// -> {sorted: {pets: true}}

store.delListener(listenerId);
Since

v2.0.0

addHasRowListener

The addHasRowListener method registers a listener function with the Store that will be called when a Row is added to or removed from the Store.

addHasRowListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: HasRowListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerHasRowListener<Store>

The function that will be called whenever the matching Row is added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasRowListener function, and will be called with a reference to the Store, the Id of the Table that changed, and the Id of the Row that changed. It is also given a flag to indicate whether the Row now exists (having not done previously), or does not (having done so previously).

You can either listen to a single Row being added or removed (by specifying the Table Id and Row Id, as the method's first two parameters) or changes to any Row (by providing null wildcards).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Row being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasRowListener(
  'pets',
  'fido',
  (store, tableId, rowId, hasRow) => {
    console.log(
      'fido row in pets table ' + (hasRow ? 'added' : 'removed'),
    );
  },
);

store.delRow('pets', 'fido');
// -> 'fido row in pets table removed'

store.setRow('pets', 'fido', {species: 'dog', color: 'brown'});
// -> 'fido row in pets table added'

store.delListener(listenerId);

This example registers a listener that responds to any Row being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasRowListener(
  null,
  null,
  (store, tableId, rowId, hasRow) => {
    console.log(
      `${rowId} row in ${tableId} table ` + (hasRow ? 'added' : 'removed'),
    );
  },
);

store.delRow('pets', 'fido');
// -> 'fido row in pets table removed'
store.setTable('species', {dog: {price: 5}});
// -> 'dog row in species table added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Row being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasRowListener(
  'pets',
  'fido',
  (store, tableId, rowId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}`, true),
  true,
);

store.delRow('pets', 'fido');
console.log(store.getTable('meta'));
// -> {update: {pets_fido: true}}

store.delListener(listenerId);
Since

v4.4.0

addRowCountListener

The addRowCountListener method registers a listener function with the Store that will be called whenever the count of Row objects in a Table change.

addRowCountListener(
  tableId: IdOrNull,
  listener: RowCountListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerRowCountListener<Store>

The function that will be called whenever the number of Row objects in the Table changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a RowCountListener function, and will be called with a reference to the Store, the Id of the Table that changed, and the number of Row objects in the Table.

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a change in the number of Row objects in a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowCountListener(
  'pets',
  (store, _tableId, count) => {
    console.log('Row count for pets table changed to ' + count);
  },
);

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Row count for pets table changed to 2'

store.delListener(listenerId);

This example registers a listener that responds to any change to a change in the number of Row objects of any Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowCountListener(
  null,
  (store, tableId, count) => {
    console.log(`Row count for ${tableId} table changed to ${count}`);
  },
);

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Row count for pets table changed to 2'
store.setRow('species', 'dog', {price: 5});
// -> 'Row count for species table changed to 1'

store.delListener(listenerId);

This example registers a listener that responds to any change to a change in the number of Row objects of a specific Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowCountListener(
  'pets',
  (store, tableId, count) =>
    store.setCell('meta', 'update', tableId, count),
  true, // mutator
);

store.setRow('pets', 'felix', {species: 'cat'});
console.log(store.getTable('meta'));
// -> {update: {pets: 2}}

store.delListener(listenerId);
Since

v4.1.0

addRowListener

The addRowListener method registers a listener function with the Store that will be called whenever data in a Row changes.

addRowListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: RowListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerRowListener<Store>

The function that will be called whenever data in the matching Row changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a RowListener function, and will be called with a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, and a GetCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single Row (by specifying the Table Id and Row Id as the method's first two parameters) or changes to any Row (by providing null wildcards).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to a specific Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addRowListener(
  'pets',
  'fido',
  (store, tableId, rowId, getCellChange) => {
    console.log('fido row in pets table changed');
    console.log(getCellChange('pets', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'fido row in pets table changed'
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addRowListener(
  null,
  null,
  (store, tableId, rowId) => {
    console.log(`${rowId} row in ${tableId} table changed`);
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'fido row in pets table changed'
store.setTable('species', {dog: {price: 5}});
// -> 'dog row in species table changed'

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Row, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addRowListener(
  'pets',
  'fido',
  (store, tableId, rowId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}`, true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {pets_fido: true}}

store.delListener(listenerId);
Since

v1.0.0

addCellIdsListener

The addCellIdsListener method registers a listener function with the Store that will be called whenever the Cell Ids in a Row change.

addCellIdsListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: CellIdsListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerCellIdsListener<Store>

The function that will be called whenever the Cell Ids in the Row change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a CellIdsListener function, and will be called with a reference to the Store, the Id of the Table, and the Id of the Row that changed.

By default, such a listener is only called when a Cell is added or removed. To listen to all changes in the Row, use the addRowListener method.

You can either listen to a single Row (by specifying the Table Id and Row Id as the method's first two parameters) or changes to any Row (by providing a null wildcard).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Cell Ids of a specific Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addCellIdsListener('pets', 'fido', (store) => {
  console.log('Cell Ids for fido row in pets table changed');
  console.log(store.getCellIds('pets', 'fido'));
});

store.setCell('pets', 'fido', 'color', 'brown');
// -> 'Cell Ids for fido row in pets table changed'
// -> ['species', 'color']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Cell Ids of any Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addCellIdsListener(
  null,
  null,
  (store, tableId, rowId) => {
    console.log(`Cell Ids for ${rowId} row in ${tableId} table changed`);
    console.log(store.getCellIds(tableId, rowId));
  },
);

store.setCell('pets', 'fido', 'color', 'brown');
// -> 'Cell Ids for fido row in pets table changed'
// -> ['species', 'color']
store.setCell('species', 'dog', 'price', 5);
// -> 'Cell Ids for dog row in species table changed'
// -> ['price']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Cell Ids of a specific Row, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addCellIdsListener(
  'pets',
  'fido',
  (store, tableId, rowId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}`, true),
  true, // mutator
);

store.setCell('pets', 'fido', 'color', 'brown');
console.log(store.getTable('meta'));
// -> {update: {pets_fido: true}}

store.delListener(listenerId);
Since

v1.0.0

addCellListener

The addCellListener method registers a listener function with the Store that will be called whenever data in a Cell changes.

addCellListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: CellListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerCellListener<Store>

The function that will be called whenever data in the matching Cell changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a CellListener function, and will be called with a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, the Id of the Cell that changed, the new Cell value, the old Cell value, and a GetCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single Cell (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or changes to any Cell (by providing null wildcards).

All, some, or none of the tableId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific Row in a specific Table, any Cell in any Row in any Table, for example - or every other combination of wildcards.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to a specific Cell.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, newCell, oldCell, getCellChange) => {
    console.log('color cell in fido row in pets table changed');
    console.log([oldCell, newCell]);
    console.log(getCellChange('pets', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'color cell in fido row in pets table changed'
// -> ['brown', 'walnut']
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any Cell.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId) => {
    console.log(
      `${cellId} cell in ${rowId} row in ${tableId} table changed`,
    );
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'color cell in fido row in pets table changed'
store.setTable('species', {dog: {price: 5}});
// -> 'price cell in dog row in species table changed'

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Cell, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}_${cellId}`, true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {pets_fido_color: true}}

store.delListener(listenerId);
Since

v1.0.0

addHasCellListener

The addHasCellListener method registers a listener function with the Store that will be called when a Cell is added to or removed from the Store.

addHasCellListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: HasCellListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerHasCellListener<Store>

The function that will be called whenever the matching Cell is added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasCellListener function, and will be called with a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, and the Id of the Cell that changed. It is also given a flag to indicate whether the Cell now exists (having not done previously), or does not (having done so previously).

You can either listen to a single Cell being added or removed (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or changes to any Cell (by providing null wildcards).

All, some, or none of the tableId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific Row in a specific Table, any Cell in any Row in any Table, for example - or every other combination of wildcards.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Cell being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, hasCell) => {
    console.log(
      'color cell in fido row in pets table ' +
        (hasCell ? 'added' : 'removed'),
    );
  },
);

store.delCell('pets', 'fido', 'color');
// -> 'color cell in fido row in pets table removed'

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'color cell in fido row in pets table added'

store.delListener(listenerId);

This example registers a listener that responds to any Cell being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId, hasCell) => {
    console.log(
      `${cellId} cell in ${rowId} row in ${tableId} table ` +
        (hasCell ? 'added' : 'removed'),
    );
  },
);

store.delCell('pets', 'fido', 'color');
// -> 'color cell in fido row in pets table removed'
store.setTable('species', {dog: {price: 5}});
// -> 'price cell in dog row in species table added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Cell being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}_${cellId}`, true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {pets_fido_color: true}}

store.delListener(listenerId);
Since

v4.4.0

addInvalidCellListener

The addInvalidCellListener method registers a listener function with the Store that will be called whenever invalid data was attempted to be written to a Cell.

addInvalidCellListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: InvalidCellListener<Store>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerInvalidCellListener<Store>

The function that will be called whenever an attempt to write invalid data to the matching Cell was made.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is an InvalidCellListener function, and will be called with a reference to the Store, the Id of the Table, the Id of the Row, and the Id of Cell that was being attempted to be changed. It is also given the invalid value of the Cell, which could have been of absolutely any type. Since there could have been multiple failed attempts to set the Cell within a single transaction, this is an array containing each attempt, chronologically.

You can either listen to a single Cell (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or invalid attempts to change any Cell (by providing null wildcards).

All, some, or none of the tableId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific Row in a specific Table, any Cell in any Row in any Table, for example - or every other combination of wildcards.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Special note should be made for how the listener will be called when a TablesSchema is present. The listener will be called:

The listener will not be called if a Cell that is defaulted in the TablesSchema is not provided, as long as all of the Cells that are not defaulted are provided.

To help understand all of these schema-based conditions, please see the TablesSchema example below.

Examples

This example registers a listener that responds to any invalid changes to a specific Cell.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addInvalidCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, invalidCells) => {
    console.log('Invalid color cell in fido row in pets table');
    console.log(invalidCells);
  },
);

store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
// -> 'Invalid color cell in fido row in pets table'
// -> [{r: '96', g: '4B', b: '00'}]

store.delListener(listenerId);

This example registers a listener that responds to any invalid changes to any Cell - in a Store without a TablesSchema. Note also how it then responds to cases where empty or invalid Row objects, or Table objects, or Tables objects are provided.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addInvalidCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId) => {
    console.log(
      `Invalid ${cellId} cell in ${rowId} row in ${tableId} table`,
    );
  },
);

store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
// -> 'Invalid color cell in fido row in pets table'
store.setTable('sales', {fido: {date: new Date()}});
// -> 'Invalid date cell in fido row in sales table'

store.setRow('pets', 'felix', {});
// -> 'Invalid undefined cell in felix row in pets table'

store.setRow('filter', 'name', /[a-z]?/);
// -> 'Invalid undefined cell in name row in filter table'

store.setRow('sales', '2021', {forecast: undefined});
// -> 'Invalid forecast cell in 2021 row in sales table'

store.addRow('filter', /[0-9]?/);
// -> 'Invalid undefined cell in undefined row in filter table'

store.setTable('raw', {});
// -> 'Invalid undefined cell in undefined row in raw table'

store.setTable('raw', ['row1', 'row2']);
// -> 'Invalid undefined cell in undefined row in raw table'

store.setTables(['table1', 'table2']);
// -> 'Invalid undefined cell in undefined row in undefined table'

store.delListener(listenerId);

This example registers a listener that responds to any invalid changes to any Cell - in a Store with a TablesSchema. Note how it responds to cases where missing parameters are provided for optional, and defaulted Cell values in a Row.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {
    species: {type: 'string'},
    color: {type: 'string', default: 'unknown'},
  },
});

const listenerId = store.addInvalidCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId) => {
    console.log(
      `Invalid ${cellId} cell in ${rowId} row in ${tableId} table`,
    );
  },
);

store.setRow('sales', 'fido', {price: 5});
// -> 'Invalid price cell in fido row in sales table'
// The listener is called, because the sales Table is not in the schema

store.setRow('pets', 'felix', {species: true});
// -> 'Invalid species cell in felix row in pets table'
// The listener is called, because species is invalid...
console.log(store.getRow('pets', 'felix'));
// -> {color: 'unknown'}
// ...even though a Row was set with the default value

store.setRow('pets', 'fido', {color: 'brown'});
// -> 'Invalid species cell in fido row in pets table'
// The listener is called, because species is missing and not defaulted...
console.log(store.getRow('pets', 'fido'));
// -> {color: 'brown'}
// ...even though a Row was set

store.setRow('pets', 'rex', {species: 'dog'});
console.log(store.getRow('pets', 'rex'));
// -> {species: 'dog', color: 'unknown'}
// The listener is not called, because color is defaulted

store.delTables().setTablesSchema({
  pets: {
    species: {type: 'string'},
    color: {type: 'string'},
  },
});

store.setRow('pets', 'cujo', {});
// -> 'Invalid species cell in cujo row in pets table'
// -> 'Invalid color cell in cujo row in pets table'
// -> 'Invalid undefined cell in cujo row in pets table'
// The listener is called multiple times, because neither Cell is defaulted
// and the Row as a whole is empty

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Cell, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addInvalidCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, invalidCells) =>
    store.setCell(
      'meta',
      'invalid_updates',
      `${tableId}_${rowId}_${cellId}`,
      JSON.stringify(invalidCells[0]),
    ),
  true,
);

store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
console.log(store.getRow('meta', 'invalid_updates'));
// -> {'pets_fido_color': '{"r":"96","g":"4B","b":"00"}'}

store.delListener(listenerId);
Since

v1.1.0

addHasValuesListener

The addHasValuesListener method registers a listener function with the Store that will be called when Values as a whole are added to or removed from the Store.

addHasValuesListener(
  listener: HasValuesListener<Store>,
  mutator?: boolean,
): string
TypeDescription
listenerHasValuesListener<Store>

The function that will be called whenever Values as a whole are added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasValuesListener function, and will be called with a reference to the Store. It is also given a flag to indicate whether Values now exist (having not done previously), or do not (having done so previously).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to Values being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addHasValuesListener((store, hasValues) => {
  console.log('Values ' + (hasValues ? 'added' : 'removed'));
});

store.delValues();
// -> 'Values removed'

store.setValue('employees', 4);
// -> 'Values added'

store.delListener(listenerId);

This example registers a listener that responds to Values being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore();
const listenerId = store.addHasValuesListener(
  (store, hasValues) => store.setValue('hasValues', hasValues),
  true,
);

store.setValue('employees', 4);
console.log(store.getValues());
// -> {employees: 4, hasValues: true}

store.delListener(listenerId);
Since

v4.4.0

addValuesListener

The addValuesListener method registers a listener function with the Store that will be called whenever the Values change.

addValuesListener(
  listener: ValuesListener<Store>,
  mutator?: boolean,
): string
TypeDescription
listenerValuesListener<Store>

The function that will be called whenever data in the Values changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a ValuesListener function, and will be called with a reference to the Store and a GetValueChange function in case you need to inspect any changes that occurred.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to the Store's Values.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValuesListener((store, getValueChange) => {
  console.log('values changed');
  console.log(getValueChange('employees'));
});

store.setValue('employees', 4);
// -> 'values changed'
// -> [true, 3, 4]

store.delListener(listenerId);

This example registers a listener that responds to any changes to the Store's Values, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValuesListener(
  (store) => store.setValue('updated', true),
  true,
);

store.setValue('employees', 4);
console.log(store.getValues());
// -> {open: true, employees: 4, updated: true}

store.delListener(listenerId);
Since

v3.0.0

addHasValueListener

The addHasValueListener method registers a listener function with the Store that will be called when a Value is added to or removed from the Store.

addHasValueListener(
  valueId: IdOrNull,
  listener: HasValueListener<Store>,
  mutator?: boolean,
): string
TypeDescription
valueIdIdOrNull

The Id of the Value to listen to, or null as a wildcard.

listenerHasValueListener<Store>

The function that will be called whenever the matching Value is added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasValueListener function, and will be called with a reference to the Store and the Id of Value that changed. It is also given a flag to indicate whether the Value now exists (having not done previously), or does not (having done so previously).

You can either listen to a single Value being added or removed (by specifying the Value Id) or any Value being added or removed (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Value being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addHasValueListener(
  'employees',
  (store, valueId, hasValue) => {
    console.log('employee value ' + (hasValue ? 'added' : 'removed'));
  },
);

store.delValue('employees');
// -> 'employee value removed'

store.setValue('employees', 4);
// -> 'employee value added'

store.delListener(listenerId);

This example registers a listener that responds to any Value being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addHasValueListener(
  null,
  (store, valueId, hasValue) => {
    console.log(valueId + ' value ' + (hasValue ? 'added' : 'removed'));
  },
);

store.delValue('employees');
// -> 'employees value removed'
store.setValue('website', 'https://pets.com');
// -> 'website value added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Value being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addHasValueListener(
  'employees',
  (store) => store.setValue('updated', true),
  true,
);

store.delValue('employees');
console.log(store.getValues());
// -> {open: true, updated: true}

store.delListener(listenerId);
Since

v4.4.0

addInvalidValueListener

The addInvalidValueListener method registers a listener function with the Store that will be called whenever invalid data was attempted to be written to a Value.

addInvalidValueListener(
  valueId: IdOrNull,
  listener: InvalidValueListener<Store>,
  mutator?: boolean,
): string
TypeDescription
valueIdIdOrNull

The Id of the Value to listen to, or null as a wildcard.

listenerInvalidValueListener<Store>

The function that will be called whenever an attempt to write invalid data to the matching Value was made.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is an InvalidValueListener function, and will be called with a reference to the Store and the Id of Value that was being attempted to be changed. It is also given the invalid value of the Value, which could have been of absolutely any type. Since there could have been multiple failed attempts to set the Value within a single transaction, this is an array containing each attempt, chronologically.

You can either listen to a single Value (by specifying the Value Id as the method's first parameter) or invalid attempts to change any Value (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Special note should be made for how the listener will be called when a ValuesSchema is present. The listener will be called:

The listener will not be called if a Value that is defaulted in the ValuesSchema is not provided, as long as all of the Values that are not defaulted are provided.

To help understand all of these schema-based conditions, please see the ValuesSchema example below.

Examples

This example registers a listener that responds to any invalid changes to a specific Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addInvalidValueListener(
  'open',
  (store, valueId, invalidValues) => {
    console.log('Invalid open value');
    console.log(invalidValues);
  },
);

store.setValue('open', {yes: true});
// -> 'Invalid open value'
// -> [{yes: true}]

store.delListener(listenerId);

This example registers a listener that responds to any invalid changes to any Value - in a Store without a ValuesSchema. Note also how it then responds to cases where an empty Values object is provided.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addInvalidValueListener(
  null,
  (store, valueId) => {
    console.log(`Invalid ${valueId} value`);
  },
);

store.setValue('open', {yes: true});
// -> 'Invalid open value'
store.setValue('employees', ['alice', 'bob']);
// -> 'Invalid employees value'

store.setValues('pets', 'felix', {});
// -> 'Invalid undefined value'

store.delListener(listenerId);

This example registers a listener that responds to any invalid changes to any Value - in a Store with a ValuesSchema. Note how it responds to cases where missing parameters are provided for optional, and defaulted Values.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({
  open: {type: 'boolean', default: false},
  employees: {type: 'number'},
});

console.log(store.getValues());
// -> {open: false}

const listenerId = store.addInvalidValueListener(
  null,
  (store, valueId) => {
    console.log(`Invalid ${valueId} value`);
  },
);

store.setValue('website', true);
// -> 'Invalid website value'
// The listener is called, because the website Value is not in the schema

store.setValue('open', 'yes');
// -> 'Invalid open value'
// The listener is called, because 'open' is invalid...
console.log(store.getValues());
// -> {open: false}
// ...even though it is still present with the default value

store.setValues({open: true});
// -> 'Invalid employees value'
// The listener is called because employees is missing and not defaulted...
console.log(store.getValues());
// -> {open: true}
// ...even though the Values were set

store.setValues({employees: 3});
console.log(store.getValues());
// -> {open: false, employees: 3}
// The listener is not called, because 'open' is defaulted

store.setValuesSchema({
  open: {type: 'boolean'},
  employees: {type: 'number'},
});

store.setValues({});
// -> 'Invalid open value'
// -> 'Invalid employees value'
// -> 'Invalid undefined value'
// The listener is called multiple times, because neither Value is
// defaulted and the Values as a whole were empty

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Value, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addInvalidValueListener(
  'open',
  (store, valueId, invalidValues) =>
    store.setValue('invalid_updates', JSON.stringify(invalidValues[0])),
  true,
);

store.setValue('open', {yes: true});
console.log(store.getValue('invalid_updates'));
// -> '{"yes":true}'

store.delListener(listenerId);
Since

v3.0.0

addValueIdsListener

The addValueIdsListener method registers a listener function with the Store that will be called whenever the Value Ids in a Store change.

addValueIdsListener(
  listener: ValueIdsListener<Store>,
  mutator?: boolean,
): string
TypeDescription
listenerValueIdsListener<Store>

The function that will be called whenever the Value Ids in the Store change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a ValueIdsListener function, and will be called with a reference to the Store.

By default, such a listener is only called when a Value is added or removed. To listen to all changes in the Values, use the addValuesListener method.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Value Ids.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addValueIdsListener((store) => {
  console.log('Value Ids changed');
  console.log(store.getValueIds());
});

store.setValue('employees', 3);
// -> 'Value Ids changed'
// -> ['open', 'employees']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Value Ids, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addValueIdsListener(
  (store) => store.setValue('updated', true),
  true, // mutator
);

store.setValue('employees', 3);
console.log(store.getValues());
// -> {open: true, employees: 3, updated: true}

store.delListener(listenerId);
Since

v3.0.0

addValueListener

The addValueListener method registers a listener function with the Store that will be called whenever data in a Value changes.

addValueListener(
  valueId: IdOrNull,
  listener: ValueListener<Store>,
  mutator?: boolean,
): string
TypeDescription
valueIdIdOrNull

The Id of the Value to listen to, or null as a wildcard.

listenerValueListener<Store>

The function that will be called whenever data in the matching Value changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a ValueListener function, and will be called with a reference to the Store, the Id of the Value that changed, the new Value value, the old Value, and a GetValueChange function in case you need to inspect any changes that occurred.

You can either listen to a single Value (by specifying the Value Id) or changes to any Value (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to a specific Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValueListener(
  'employees',
  (store, valueId, newValue, oldValue, getValueChange) => {
    console.log('employee value changed');
    console.log([oldValue, newValue]);
    console.log(getValueChange('employees'));
  },
);

store.setValue('employees', 4);
// -> 'employee value changed'
// -> [3, 4]
// -> [true, 3, 4]

store.delListener(listenerId);

This example registers a listener that responds to any changes to any Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValueListener(null, (store, valueId) => {
  console.log(`${valueId} value changed`);
});

store.setValue('employees', 4);
// -> 'employees value changed'
store.setValue('open', false);
// -> 'open value changed'

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Value, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValueListener(
  'employees',
  (store) => store.setValue('updated', true),
  true,
);

store.delValue('employees');
console.log(store.getValues());
// -> {open: true, updated: true}

store.delListener(listenerId);
Since

v3.0.0

addDidFinishTransactionListener

The addDidFinishTransactionListener method registers a listener function with the Store that will be called just after other non-mutating listeners are called at the end of the transaction.

addDidFinishTransactionListener(listener: TransactionListener<Store>): string
TypeDescription
listenerTransactionListener<Store>

The function that will be called after the end of a transaction.

returnsstring

A unique Id for the listener that can later be used to remove it.

This is useful if you need to know that a set of listeners have just been called at the end of a transaction, perhaps to batch their consequences together.

The provided TransactionListener will receive a reference to the Store and two booleans to indicate whether Cell or Value data has been touched during the transaction. The two flags is intended as a hint about whether non-mutating listeners might have been called at the end of the transaction.

Here, 'touched' means that Cell or Value data has either been changed, or changed and then changed back to its original value during the transaction. The exception is a transaction that has been rolled back, for which the value of cellsTouched and valuesTouched in the listener will be false because all changes have been reverted.

Note that a TransactionListener added to the Store with this method cannot mutate the Store itself, and attempts to do so will fail silently.

Example

This example registers a listener that is called at the end of the transaction, just after its listeners have been called. The transactions shown here variously change, touch, and rollback cells, demonstrating how the cellsTouched and valuesTouched parameters in the listener work.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', color: 'brown'}},
  })
  .setValues({open: true, employees: 3});
const listenerId = store.addDidFinishTransactionListener((store) => {
  const [cellsTouched, valuesTouched] = store.getTransactionLog() ?? {};
  console.log(`Cells/Values touched: ${cellsTouched}/${valuesTouched}`);
});
const listenerId2 = store.addTablesListener(() =>
  console.log('Tables changed'),
);
const listenerId3 = store.addValuesListener(() =>
  console.log('Values changed'),
);

store.transaction(() =>
  store.setCell('pets', 'fido', 'color', 'brown').setValue('employees', 3),
);
// -> 'Cells/Values touched: false/false'

store.transaction(() => store.setCell('pets', 'fido', 'color', 'walnut'));
// -> 'Tables changed'
// -> 'Cells/Values touched: true/false'

store.transaction(() => store.setValue('employees', 4));
// -> 'Values changed'
// -> 'Cells/Values touched: false/true'

store.transaction(() => {
  store
    .setRow('pets', 'felix', {species: 'cat'})
    .delRow('pets', 'felix')
    .setValue('city', 'London')
    .delValue('city');
});
// -> 'Cells/Values touched: true/true'
// But no Tables or Values listeners fired since there are no net changes.

store.transaction(
  () =>
    store
      .setRow('pets', 'felix', {species: 'cat'})
      .setValue('city', 'London'),
  () => true,
);
// -> 'Cells/Values touched: false/false'
// Transaction was rolled back.

store.callListener(listenerId);
// -> 'Cells/Values touched: false/false'
// It is meaningless to call this listener directly.

store
  .delListener(listenerId)
  .delListener(listenerId2)
  .delListener(listenerId3);
Since

v1.3.0

addStartTransactionListener

The addStartTransactionListener method registers a listener function with the Store that will be called at the start of a transaction.

addStartTransactionListener(listener: TransactionListener<Store>): string
TypeDescription
listenerTransactionListener<Store>

The function that will be called at the start of a transaction.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided TransactionListener will receive a reference to the Store and two booleans to indicate whether Cell or Value data has been touched during the transaction. Since this is called at the start, they will both be false!

Note that a TransactionListener added to the Store with this method can mutate the Store, and its changes will be treated as part of the transaction that is starting.

Example

This example registers a listener that is called at start end of the transaction, just before its listeners will be called.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', color: 'brown'}},
  })
  .setValues({open: true, employees: 3});
const listenerId = store.addStartTransactionListener(() => {
  console.log('Transaction started');
});

store.transaction(() =>
  store.setCell('pets', 'fido', 'color', 'brown').setValue('employees', 3),
);
// -> 'Transaction started'

store.callListener(listenerId);
// -> 'Transaction started'

store.delListener(listenerId);
Since

v3.2.0

addWillFinishTransactionListener

The addWillFinishTransactionListener method registers a listener function with the Store that will be called just before other non-mutating listeners are called at the end of the transaction.

addWillFinishTransactionListener(listener: TransactionListener<Store>): string
TypeDescription
listenerTransactionListener<Store>

The function that will be called before the end of a transaction.

returnsstring

A unique Id for the listener that can later be used to remove it.

This is useful if you need to know that a set of listeners are about to be called at the end of a transaction, perhaps to batch their consequences together.

The provided TransactionListener will receive a reference to the Store and two booleans to indicate whether Cell or Value data has been touched during the transaction. The two flags are intended as a hint about whether non-mutating listeners might be being called at the end of the transaction.

Here, 'touched' means that Cell or Value data has either been changed, or changed and then changed back to its original value during the transaction. The exception is a transaction that has been rolled back, for which the value of cellsTouched and valuesTouched in the listener will be false because all changes have been reverted.

Note that a TransactionListener added to the Store with this method can mutate the Store itself, and its changes will be treated as part of the transaction that is starting (and may fire non-mutating listeners after this).

Example

This example registers a listener that is called at the end of the transaction, just before its listeners will be called. The transactions shown here variously change, touch, and rollback cells, demonstrating how the cellsTouched and valuesTouched parameters in the listener work.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', color: 'brown'}},
  })
  .setValues({open: true, employees: 3});
const listenerId = store.addWillFinishTransactionListener((store) => {
  const [cellsTouched, valuesTouched] = store.getTransactionLog() ?? {};
  console.log(`Cells/Values touched: ${cellsTouched}/${valuesTouched}`);
});
const listenerId2 = store.addTablesListener(() =>
  console.log('Tables changed'),
);
const listenerId3 = store.addValuesListener(() =>
  console.log('Values changed'),
);

store.transaction(() =>
  store.setCell('pets', 'fido', 'color', 'brown').setValue('employees', 3),
);
// -> 'Cells/Values touched: false/false'

store.transaction(() => store.setCell('pets', 'fido', 'color', 'walnut'));
// -> 'Cells/Values touched: true/false'
// -> 'Tables changed'

store.transaction(() => store.setValue('employees', 4));
// -> 'Cells/Values touched: false/true'
// -> 'Values changed'

store.transaction(() => {
  store
    .setRow('pets', 'felix', {species: 'cat'})
    .delRow('pets', 'felix')
    .setValue('city', 'London')
    .delValue('city');
});
// -> 'Cells/Values touched: true/true'
// But no Tables or Values listeners fired since there are no net changes.

store.transaction(
  () =>
    store
      .setRow('pets', 'felix', {species: 'cat'})
      .setValue('city', 'London'),
  () => true,
);
// -> 'Cells/Values touched: false/false'
// Transaction was rolled back.

store.callListener(listenerId);
// -> 'Cells/Values touched: false/false'
// It is meaningless to call this listener directly.

store
  .delListener(listenerId)
  .delListener(listenerId2)
  .delListener(listenerId3);
Since

v1.3.0

callListener

The callListener method provides a way for you to manually provoke a listener to be called, even if the underlying data hasn't changed.

callListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to call.

returnsthis

A reference to the Store.

This is useful when you are using mutator listeners to guarantee that data conforms to programmatic conditions, and those conditions change such that you need to update the Store in bulk.

Examples

This example registers a listener that ensures a Cell has one of list of a valid values. After that list changes, the listener is called to apply the condition to the existing data.

import {createStore} from 'tinybase';

const validColors = ['walnut', 'brown', 'black'];
const store = createStore();
const listenerId = store.addCellListener(
  'pets',
  null,
  'color',
  (store, tableId, rowId, cellId, color) => {
    if (!validColors.includes(color)) {
      store.setCell(tableId, rowId, cellId, validColors[0]);
    }
  },
  true,
);

store.setRow('pets', 'fido', {species: 'dog', color: 'honey'});
console.log(store.getRow('pets', 'fido'));
// -> {species: 'dog', color: 'walnut'}

validColors.shift();
console.log(validColors);
// -> ['brown', 'black']

store.callListener(listenerId);
console.log(store.getRow('pets', 'fido'));
// -> {species: 'dog', color: 'brown'}

store.delListener(listenerId);

This example registers a listener to Row Id changes. It is explicitly called and fires for two Tables in the Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});

const listenerId = store.addRowIdsListener(null, (store, tableId) => {
  console.log(`Row Ids listener called for ${tableId} table`);
});

store.callListener(listenerId);
// -> 'Row Ids listener called for pets table'
// -> 'Row Ids listener called for species table'

store.delListener(listenerId);

This example registers a listener Value changes. It is explicitly called and fires for two Values in the Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});

const listenerId = store.addValueListener(
  null,
  (store, valueId, value) => {
    console.log(`Value listener called for ${valueId} value, ${value}`);
  },
);

store.callListener(listenerId);
// -> 'Value listener called for open value, true'
// -> 'Value listener called for employees value, 3'

store.delListener(listenerId);

This example registers listeners for the end of transactions, and for invalid Cells. They are explicitly called, meaninglessly. The former receives empty arguments. The latter is not called at all.

import {createStore} from 'tinybase';

const store = createStore();

const listenerId = store.addWillFinishTransactionListener(
  (store, cellsTouched, valuesTouched) => {
    console.log(`Transaction finish: ${cellsTouched}/${valuesTouched}`);
  },
);
store.callListener(listenerId);
// -> 'Transaction finish: undefined/undefined'
store.delListener(listenerId);

const listenerId2 = store.addInvalidCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId) => {
    console.log('Invalid cell', tableId, rowId, cellId);
  },
);
store.callListener(listenerId2);
// -> undefined
store.delListener(listenerId2);
Since

v1.0.0

delListener

The delListener method removes a listener that was previously added to the Store.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Store.

Use the Id returned by whichever method was used to add the listener. Note that the Store may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTablesListener(() => {
  console.log('Tables changed');
});

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Tables changed'

store.delListener(listenerId);

store.setCell('pets', 'fido', 'color', 'honey');
// -> undefined
// The listener is not called.
Since

v1.0.0

Iterator methods

This is the collection of iterator methods within the Store interface. There are 5 iterator methods in total.

forEachTable

The forEachTable method takes a function that it will then call for each Table in the Store.

forEachTable(tableCallback: TableCallback): void
TypeDescription
tableCallbackTableCallback

The function that should be called for every Table.

returnsvoid

This has no return value.

This method is useful for iterating over the Table structure of the Store in a functional style. The tableCallback parameter is a TableCallback function that will be called with the Id of each Table, and with a function that can then be used to iterate over each Row of the Table, should you wish.

Example

This example iterates over each Table in a Store, and lists each Row Id within them.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
store.forEachTable((tableId, forEachRow) => {
  console.log(tableId);
  forEachRow((rowId) => console.log(`- ${rowId}`));
});
// -> 'pets'
// -> '- fido'
// -> 'species'
// -> '- dog'
Since

v1.0.0

forEachTableCell

The forEachTableCell method takes a function that it will then call for each Cell used across the whole Table.

forEachTableCell(
  tableId: string,
  tableCellCallback: TableCellCallback,
): void
TypeDescription
tableIdstring

The Id of the Table containing the Cells to iterate over.

tableCellCallbackTableCellCallback

The function that should be called for every Cell Id used across the whole Table.

returnsvoid

This has no return value.

This method is useful for iterating over the Cell structure of the Table in a functional style. The tableCellCallback parameter is a TableCellCallback function that will be called with the Id of each Cell and the count of Rows in the Table in which it appears.

Example

This example iterates over each Cell Id used across the whole Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}, felix: {species: 'cat', legs: 4}},
});
store.forEachTableCell('pets', (cellId, count) => {
  console.log(`${cellId}: ${count}`);
});
// -> 'species: 2'
// -> 'legs: 1'
Since

v3.3.0

forEachRow

The forEachRow method takes a function that it will then call for each Row in a specified Table.

forEachRow(
  tableId: string,
  rowCallback: RowCallback,
): void
TypeDescription
tableIdstring

The Id of the Table to iterate over.

rowCallbackRowCallback

The function that should be called for every Row.

returnsvoid

This has no return value.

This method is useful for iterating over the Row structure of the Table in a functional style. The rowCallback parameter is a RowCallback function that will be called with the Id of each Row, and with a function that can then be used to iterate over each Cell of the Row, should you wish.

Example

This example iterates over each Row in a Table, and lists each Cell Id within them.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {color: 'black'},
  },
});
store.forEachRow('pets', (rowId, forEachCell) => {
  console.log(rowId);
  forEachCell((cellId) => console.log(`- ${cellId}`));
});
// -> 'fido'
// -> '- species'
// -> 'felix'
// -> '- color'
Since

v1.0.0

forEachCell

The forEachCell method takes a function that it will then call for each Cell in a specified Row.

forEachCell(
  tableId: string,
  rowId: string,
  cellCallback: CellCallback,
): void
TypeDescription
tableIdstring

The Id of the Table containing the Row to iterate over.

rowIdstring

The Id of the Row to iterate over.

cellCallbackCellCallback

The function that should be called for every Cell.

returnsvoid

This has no return value.

This method is useful for iterating over the Cell structure of the Row in a functional style. The cellCallback parameter is a CellCallback function that will be called with the Id and value of each Cell.

Example

This example iterates over each Cell in a Row, and lists its value.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
store.forEachCell('pets', 'fido', (cellId, cell) => {
  console.log(`${cellId}: ${cell}`);
});
// -> 'species: dog'
// -> 'color: brown'
Since

v1.0.0

forEachValue

The forEachValue method takes a function that it will then call for each Value in a Store.

forEachValue(valueCallback: ValueCallback): void
TypeDescription
valueCallbackValueCallback

The function that should be called for every Value.

returnsvoid

This has no return value.

This method is useful for iterating over the Value structure of the Store in a functional style. The valueCallback parameter is a ValueCallback function that will be called with the Id and value of each Value.

Example

This example iterates over each Value in a Store, and lists its value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
store.forEachValue((valueId, value) => {
  console.log(`${valueId}: ${value}`);
});
// -> 'open: true'
// -> 'employees: 3'
Since

v3.0.0

Transaction methods

This is the collection of transaction methods within the Store interface. There are 5 transaction methods in total.

finishTransaction

The finishTransaction method allows you to explicitly finish a transaction that has made multiple mutations to the Store, triggering all calls to the relevant listeners.

finishTransaction(doRollback?: DoRollback): this
TypeDescription
doRollback?DoRollback

An optional callback that should return true if you want to rollback the transaction at the end.

returnsthis

A reference to the Store.

Transactions are useful for making bulk changes to the data in a Store, and when you don't want listeners to be called as you make each change. Changes are made silently during the transaction, and listeners relevant to the changes you have made will instead only be called when the whole transaction is complete.

Generally it is preferable to use the transaction method to wrap a block of code as a transaction. It simply calls both the startTransaction and finishTransaction methods for you. See that method for several transaction examples.

Use this finishTransaction method when you have a more 'open-ended' transaction, such as one containing mutations triggered from other events that are asynchronous or not occurring inline to your code. There must have been a corresponding startTransaction method that this completes, of course, otherwise this function has no effect.

The optional parameter, doRollback is a DoRollback callback that you can use to rollback the transaction if it did not complete to your satisfaction. It is called with getTransactionChanges and getTransactionLog parameters, which inform you of the net changes that have been made during the transaction, at different levels of detail. See the DoRollback documentation for more details.

Examples

This example makes changes to two Cells, first outside, and secondly within, a transaction that is explicitly started and finished. In the second case, the Row listener is only called once.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
store.addRowListener('pets', 'fido', () => console.log('Fido changed'));

store
  .setCell('pets', 'fido', 'color', 'brown')
  .setCell('pets', 'fido', 'sold', false);
// -> 'Fido changed'
// -> 'Fido changed'

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'walnut')
  .setCell('pets', 'fido', 'sold', true)
  .finishTransaction();
// -> 'Fido changed'

This example makes multiple changes to the Store, including some attempts to update a Cell with invalid values. The doRollback callback receives information about the changes and invalid attempts, and then judges that the transaction should be rolled back to its original state.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'black')
  .setCell('pets', 'fido', 'eyes', ['left', 'right'])
  .setCell('pets', 'fido', 'info', {sold: null})
  .setValue('open', false)
  .setValue('employees', ['alice', 'bob'])
  .finishTransaction(() => {
    const [, , changedCells, invalidCells, changedValues, invalidValues] =
      store.getTransactionLog();
    console.log(store.getTables());
    console.log(changedCells);
    console.log(invalidCells);
    console.log(changedValues);
    console.log(invalidValues);
    return invalidCells['pets'] != null;
  });
// -> {pets: {fido: {species: 'dog', color: 'black'}}}
// -> {pets: {fido: {color: ['brown', 'black']}}}
// -> {pets: {fido: {eyes: [['left', 'right']], info: [{sold: null}]}}}
// -> {open: [true, false]}
// -> {employees: [['alice', 'bob']]}

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
console.log(store.getValues());
// -> {open: true}
Since

v1.3.0

getTransactionChanges

The getTransactionChanges method returns the net meaningful changes that have been made to a Store during a transaction.

getTransactionChanges(): Changes
returnsChanges

A Changes object representing the changes.

This is useful for deciding whether to rollback a transaction, for example. The returned object is only meaningful if the method is called when the Store is in a transaction - such as in a TransactionListener.

Example

This example makes changes to the Store. At the end of the transaction, detail about what changed is enumerated.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'black')
  .setValue('open', false)
  .finishTransaction(() => {
    const [changedCells, changedValues] = store.getTransactionChanges();
    console.log(changedCells);
    console.log(changedValues);
  });
// -> {pets: {fido: {color: 'black'}}}
// -> {open: false}
Since

v5.0.0

getTransactionLog

The getTransactionLog method returns the changes that were made to a Store during a transaction in more detail, including invalid changes, and what previous values were.

getTransactionLog(): TransactionLog
returnsTransactionLog

A TransactionLog object representing the changes.

This is useful for deciding whether to rollback a transaction, for example. The returned object is only meaningful if the method is called when the Store is in a transaction - such as in a TransactionListener.

Example

This example makes changes to the Store. At the end of the transaction, detail about what changed is enumerated.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'black')
  .setCell('pets', 'fido', 'eyes', ['left', 'right'])
  .setCell('pets', 'fido', 'info', {sold: null})
  .setValue('open', false)
  .setValue('employees', ['alice', 'bob'])
  .finishTransaction(() => {
    const [, , changedCells, invalidCells, changedValues, invalidValues] =
      store.getTransactionLog();
    console.log(changedCells);
    console.log(invalidCells);
    console.log(changedValues);
    console.log(invalidValues);
  });
// -> {pets: {fido: {color: ['brown', 'black']}}}
// -> {pets: {fido: {eyes: [['left', 'right']], info: [{sold: null}]}}}
// -> {open: [true, false]}
// -> {employees: [['alice', 'bob']]}
Since

v5.0.0

startTransaction

The startTransaction method allows you to explicitly start a transaction that will make multiple mutations to the Store, buffering all calls to the relevant listeners until it completes when you call the finishTransaction method.

startTransaction(): this
returnsthis

A reference to the Store.

Transactions are useful for making bulk changes to the data in a Store, and when you don't want listeners to be called as you make each change. Changes are made silently during the transaction, and listeners relevant to the changes you have made will instead only be called when the whole transaction is complete.

Generally it is preferable to use the transaction method to wrap a block of code as a transaction. It simply calls both the startTransaction and finishTransaction methods for you. See that method for several transaction examples.

Use this startTransaction method when you have a more 'open-ended' transaction, such as one containing mutations triggered from other events that are asynchronous or not occurring inline to your code. You must remember to also call the finishTransaction method explicitly when it is done, of course.

Example

This example makes changes to two Cells, first outside, and secondly within, a transaction that is explicitly started and finished. In the second case, the Row listener is only called once.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
store.addRowListener('pets', 'fido', () => console.log('Fido changed'));

store
  .setCell('pets', 'fido', 'color', 'brown')
  .setCell('pets', 'fido', 'sold', false);
// -> 'Fido changed'
// -> 'Fido changed'

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'walnut')
  .setCell('pets', 'fido', 'sold', true)
  .finishTransaction();
// -> 'Fido changed'
Since

v1.3.0

transaction

The transaction method takes a function that makes multiple mutations to the Store, buffering all calls to the relevant listeners until it completes.

transaction<Return>(
  actions: () => Return,
  doRollback?: DoRollback,
): Return
TypeDescription
actions() => Return

The function to be executed as a transaction.

doRollback?DoRollback

An optional callback that should return true if you want to rollback the transaction at the end. Since v1.2.

returnsReturn

Whatever value the provided transaction function returns.

This method is useful for making bulk changes to the data in a Store, and when you don't want listeners to be called as you make each change. Changes are made silently during the transaction, and listeners relevant to the changes you have made will instead only be called when the whole transaction is complete.

If multiple changes are made to a piece of Store data throughout the transaction, a relevant listener will only be called with the final value (assuming it is different to the value at the start of the transaction), regardless of the changes that happened in between. For example, if a Cell had a value 'a' and then, within a transaction, it was changed to 'b' and then 'c', any CellListener registered for that cell would be called once as if there had been a single change from 'a' to 'c'.

Transactions can be nested. Relevant listeners will be called only when the outermost one completes.

The second, optional parameter, doRollback is a DoRollback callback that you can use to rollback the transaction if it did not complete to your satisfaction. See the DoRollback documentation for more details.

Examples

This example makes changes to two Cells, first outside, and secondly within, a transaction. In the second case, the Row listener is only called once.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
store.addRowListener('pets', 'fido', () => console.log('Fido changed'));

store
  .setCell('pets', 'fido', 'color', 'brown')
  .setCell('pets', 'fido', 'sold', false);
// -> 'Fido changed'
// -> 'Fido changed'

store.transaction(() =>
  store
    .setCell('pets', 'fido', 'color', 'walnut')
    .setCell('pets', 'fido', 'sold', true),
);
// -> 'Fido changed'

This example makes multiple changes to one Cell. The Cell listener is called once - and with the final value - only if there is a net overall change.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
store.addCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, newCell) => console.log(newCell),
);

store.transaction(() =>
  store
    .setCell('pets', 'fido', 'color', 'black')
    .setCell('pets', 'fido', 'color', 'brown')
    .setCell('pets', 'fido', 'color', 'walnut'),
);
// -> 'walnut'

store.transaction(() =>
  store
    .setCell('pets', 'fido', 'color', 'black')
    .setCell('pets', 'fido', 'color', 'walnut'),
);
// -> undefined
// No net change during the transaction, so the listener is not called.

This example makes multiple changes to the Store, including some attempts to update a Cell and Value with invalid values. The doRollback callback receives information about the changes and invalid attempts, and then judges that the transaction should be rolled back to its original state.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store.transaction(
  () =>
    store
      .setCell('pets', 'fido', 'color', 'black')
      .setCell('pets', 'fido', 'eyes', ['left', 'right'])
      .setCell('pets', 'fido', 'info', {sold: null})
      .setValue('open', false)
      .setValue('employees', ['alice', 'bob']),
  () => {
    const [, , changedCells, invalidCells, changedValues, invalidValues] =
      store.getTransactionLog();
    console.log(store.getTables());
    console.log(changedCells);
    console.log(invalidCells);
    console.log(changedValues);
    console.log(invalidValues);
    return invalidCells['pets'] != null;
  },
);
// -> {pets: {fido: {species: 'dog', color: 'black'}}}
// -> {pets: {fido: {color: ['brown', 'black']}}}
// -> {pets: {fido: {eyes: [['left', 'right']], info: [{sold: null}]}}}
// -> {open: [true, false]}
// -> {employees: [['alice', 'bob']]}

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
console.log(store.getValues());
// -> {open: true}
Since

v1.0.0

Deleter methods

This is the collection of deleter methods within the Store interface. There are 9 deleter methods in total.

delTables

The delTables method lets you remove all of the data in a Store.

delTables(): this
returnsthis

A reference to the Store.

Example

This example removes the data of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.delTables();
console.log(store.getTables());
// -> {}
Since

v1.0.0

delTablesSchema

The delTablesSchema method lets you remove the TablesSchema of the Store.

delTablesSchema(): this
returnsthis

A reference to the Store.

Example

This example removes the TablesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {species: {type: 'string'}},
});
store.delTablesSchema();
console.log(store.getTablesSchemaJson());
// -> '{}'
Since

v1.0.0

delTable

The delTable method lets you remove a single Table from the Store.

delTable(tableId: string): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsthis

A reference to the Store.

Example

This example removes a Table from a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
store.delTable('pets');

console.log(store.getTables());
// -> {species: {dog: {price: 5}}}
Since

v1.0.0

delRow

The delRow method lets you remove a single Row from a Table.

delRow(
  tableId: string,
  rowId: string,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

returnsthis

A reference to the Store.

If this is the last Row in its Table, then that Table will be removed.

Example

This example removes a Row from a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}, felix: {species: 'cat'}},
});
store.delRow('pets', 'fido');

console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
Since

v1.0.0

delCell

The delCell method lets you remove a single Cell from a Row.

delCell(
  tableId: string,
  rowId: string,
  cellId: string,
  forceDel?: boolean,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

cellIdstring

The Id of the Cell in the Row.

forceDel?boolean

An optional flag to indicate that the whole Row should be deleted, even if a TablesSchema provides a default value for this Cell. Defaults to false.

returnsthis

A reference to the Store.

When there is no TablesSchema applied to the Store, then if this is the last Cell in its Row, then that Row will be removed. If, in turn, that is the last Row in its Table, then that Table will be removed.

If there is a TablesSchema applied to the Store and it specifies a default value for this Cell, then deletion will result in it being set back to its default value. To override this, use the forceDel parameter, as described below.

The forceDel parameter is an optional flag that is only relevant if a TablesSchema provides a default value for this Cell. Under such circumstances, deleting a Cell value will normally restore it to the default value. If this flag is set to true, the complete removal of the Cell is instead guaranteed. But since doing do so would result in an invalid Row (according to the TablesSchema), in fact the whole Row is deleted to retain the integrity of the Table. Therefore, this flag should be used with caution.

Examples

This example removes a Cell from a Row without a TablesSchema.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', sold: true}},
});
store.delCell('pets', 'fido', 'sold');

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example removes a Cell from a Row with a TablesSchema that defaults its value.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', sold: true}},
  })
  .setTablesSchema({
    pets: {
      species: {type: 'string'},
      sold: {type: 'boolean', default: false},
    },
  });
store.delCell('pets', 'fido', 'sold');

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', sold: false}}}

This example removes a Cell from a Row with a TablesSchema that defaults its value, but uses the forceDel parameter to override it.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', sold: true}, felix: {species: 'cat'}},
  })
  .setTablesSchema({
    pets: {
      species: {type: 'string'},
      sold: {type: 'boolean', default: false},
    },
  });
store.delCell('pets', 'fido', 'sold', true);

console.log(store.getTables());
// -> {pets: {felix: {species: 'cat', sold: false}}}
Since

v1.0.0

delValues

The delValues method lets you remove all the Values from a Store.

delValues(): this
returnsthis

A reference to the Store.

If there is a ValuesSchema applied to the Store and it specifies a default value for any Value Id, then deletion will result in it being set back to its default value.

Examples

This example removes all Values from a Store without a ValuesSchema.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
store.delValues();

console.log(store.getValues());
// -> {}

This example removes all Values from a Store with a ValuesSchema that defaults one of its values.

import {createStore} from 'tinybase';

const store = createStore()
  .setValues({open: true, employees: 3})
  .setValuesSchema({
    open: {type: 'boolean', default: false},
    employees: {type: 'number'},
  });
store.delValues();

console.log(store.getValues());
// -> {open: false}
Since

v3.0.0

delValuesSchema

The delValuesSchema method lets you remove the ValuesSchema of the Store.

delValuesSchema(): this
returnsthis

A reference to the Store.

Example

This example removes the ValuesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({
  sold: {type: 'boolean', default: false},
});
store.delValuesSchema();
console.log(store.getValuesSchemaJson());
// -> '{}'
Since

v3.0.0

delValue

The delValue method lets you remove a single Value from a Store.

delValue(valueId: string): this
TypeDescription
valueIdstring

The Id of the Value in the Row.

returnsthis

A reference to the Store.

If there is a ValuesSchema applied to the Store and it specifies a default value for this Value Id, then deletion will result in it being set back to its default value.

Examples

This example removes a Value from a Store without a ValuesSchema.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
store.delValue('employees');

console.log(store.getValues());
// -> {open: true}

This example removes a Value from a Store with a ValuesSchema that defaults its value.

import {createStore} from 'tinybase';

const store = createStore()
  .setValues({open: true, employees: 3})
  .setValuesSchema({
    open: {type: 'boolean', default: false},
    employees: {type: 'number'},
  });
store.delValue('open');

console.log(store.getValues());
// -> {open: false, employees: 3}
Since

v3.0.0

delSchema

The delSchema method lets you remove both the TablesSchema and ValuesSchema of the Store.

delSchema(): this
returnsthis

A reference to the Store.

Prior to v3.0, this method removed the TablesSchema only.

Example

This example removes the TablesSchema and ValuesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore()
  .setTablesSchema({
    pets: {species: {type: 'string'}},
  })
  .setValuesSchema({
    sold: {type: 'boolean', default: false},
  });
store.delSchema();
console.log(store.getSchemaJson());
// -> '[{},{}]'
Since

v3.0.0

Development methods

This is the collection of development methods within the Store interface. There is only one method, getListenerStats.

getListenerStats

The getListenerStats method provides a set of statistics about the listeners registered with the Store, and is used for debugging purposes.

getListenerStats(): StoreListenerStats
returnsStoreListenerStats

A StoreListenerStats object containing Store listener statistics.

The StoreListenerStats object contains a breakdown of the different types of listener. Totals include both mutator and non-mutator listeners.

The method is intended to be used during development to ensure your application is not leaking listener registrations, for example.

Example

This example gets the listener statistics of a small and simple Store.

import {createStore} from 'tinybase';

const store = createStore();
store.addTablesListener(() => console.log('Tables changed'));
store.addRowIdsListener(() => console.log('Row Ids changed'));

const listenerStats = store.getListenerStats();
console.log(listenerStats.rowIds);
// -> 1
console.log(listenerStats.tables);
// -> 1
Since

v1.0.0

Properties

There is one property, isMergeable, within the Store interface.

isMergeable

This will always be false for a Store, and true for a MergeableStore.

Since

v5.0.0

Functions

There is one function, createStore, within the store module.

createStore

The createStore function creates a Store, and is the main entry point into the store module.

createStore(): Store
returnsStore

A reference to the new Store.

Since (or perhaps because) it is the most important function in the whole module, it is trivially simple.

Examples

This example creates a Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTables());
// -> {}

This example creates a Store with some initial data:

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example creates a Store with some initial data and a TablesSchema:

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog'}}})
  .setTablesSchema({
    pets: {
      species: {type: 'string'},
      sold: {type: 'boolean', default: false},
    },
  });
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', sold: false}}}
See also

The Basics guides

Since

v1.0.0

Type Aliases

These are the type aliases within the store module.

Listener type aliases

This is the collection of listener type aliases within the store module. There are 28 listener type aliases in total.

TablesListener

The TablesListener type describes a function that is used to listen to changes to the tabular part of the Store.

(
  store: Store,
  getCellChange: GetCellChange | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

getCellChangeGetCellChange | undefined

A function that returns information about any Cell's changes.

returnsvoid

This has no return value.

A TablesListener is provided when using the addTablesListener method. See that method for specific examples.

When called, a TablesListener is given a reference to the Store and a GetCellChange function that can be used to query Cell values before and after the current transaction.

Note that if the listener was manually forced to be called (with the callListener method rather than due to a real change in the Store), the GetCellChange function will not be present.

Since

v1.0.0

HasTablesListener

The HasTablesListener type describes a function that is used to listen to Tables as a whole being added to or removed from the Store.

(
  store: Store,
  hasTables: boolean,
): void
TypeDescription
storeStore

A reference to the Store that changed.

hasTablesboolean

Whether Tables now exist or not.

returnsvoid

This has no return value.

A HasTablesListener is provided when using the addHasTablesListener method. See that method for specific examples.

When called, a HasTablesListener is given a reference to the Store. It is also given a flag to indicate whether Tables now exist (having not done previously), or do not (having done so previously).

Since

v4.4.0

TableIdsListener

The TableIdsListener type describes a function that is used to listen to changes to the Table Ids in the Store.

(
  store: Store,
  getIdChanges: GetIdChanges | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

getIdChangesGetIdChanges | undefined

A function that returns information about the Id changes, since v3.3.

returnsvoid

This has no return value.

A TableIdsListener is provided when using the addTableIdsListener method. See that method for specific examples.

When called, a TableIdsListener is given a reference to the Store.

Since v3.3, the listener is also passed a GetIdChanges function that can be used to query which Ids changed during the transaction.

Since

v1.0.0

TableCellIdsListener

The TableCellIdsListener type describes a function that is used to listen to changes to the Cell Ids that appear anywhere in a Table.

(
  store: Store,
  tableId: Id,
  getIdChanges: GetIdChanges | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

getIdChangesGetIdChanges | undefined

A function that returns information about the Id changes.

returnsvoid

This has no return value.

A TableCellIdsListener is provided when using the addTableCellIdsListener method. See that method for specific examples.

When called, a TableCellIdsListener is given a reference to the Store, the Id of the Table whose Cell Ids changed, and a GetIdChanges function that can be used to query which Ids changed during the transaction.

Since

v3.3.0

TableListener

The TableListener type describes a function that is used to listen to changes to a Table.

(
  store: Store,
  tableId: Id,
  getCellChange: GetCellChange | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

getCellChangeGetCellChange | undefined

A function that returns information about any Cell's changes.

returnsvoid

This has no return value.

A TableListener is provided when using the addTableListener method. See that method for specific examples.

When called, a TableListener is given a reference to the Store, the Id of the Table that changed, and a GetCellChange function that can be used to query Cell values before and after the current transaction.

Note that if the listener was manually forced to be called (with the callListener method rather than due to a real change in the Store), the GetCellChange function will not be present.

Since

v1.0.0

HasTableCellListener

The HasTableCellListener type describes a function that is used to listen to a Cell being added to or removed from a Table as a whole.

(
  store: Store,
  tableId: Id,
  cellId: Id,
  hasTableCell: boolean,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

cellIdId

The Id of the Table Cell that changed.

hasTableCellboolean

Whether the Cell now exists anywhere in the Table or not.

returnsvoid

This has no return value.

A HasTableCellListener is provided when using the addHasTableCellListener method. See that method for specific examples.

When called, a HasTableCellListener is given a reference to the Store, the Id of the Table that changed, and the Id of the Cell that changed. It is also given a flag to indicate whether the Cell now exists anywhere in the Table (having not done previously), or does not (having done so previously).

Since

v4.4.0

HasTableListener

The HasTableListener type describes a function that is used to listen to a Table being added to or removed from the Store.

(
  store: Store,
  tableId: Id,
  hasTable: boolean,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

hasTableboolean

Whether the Table now exists or not.

returnsvoid

This has no return value.

A HasTableListener is provided when using the addHasTableListener method. See that method for specific examples.

When called, a HasTableListener is given a reference to the Store, and the Id of the Table that changed. It is also given a flag to indicate whether the Table now exists (having not done previously), or does not (having done so previously).

Since

v4.4.0

RowIdsListener

The RowIdsListener type describes a function that is used to listen to changes to the Row Ids in a Table.

(
  store: Store,
  tableId: Id,
  getIdChanges: GetIdChanges | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

getIdChangesGetIdChanges | undefined

A function that returns information about the Id changes, since v3.3.

returnsvoid

This has no return value.

A RowIdsListener is provided when using the addRowIdsListener method. See that method for specific examples.

When called, a RowIdsListener is given a reference to the Store, and the Id of the Table whose Row Ids changed.

Since v3.3, the listener is also passed a GetIdChanges function that can be used to query which Ids changed during the transaction.

Since

v1.0.0

SortedRowIdsListener

The SortedRowIdsListener type describes a function that is used to listen to changes to sorted Row Ids in a Table.

(
  store: Store,
  tableId: Id,
  cellId: Id | undefined,
  descending: boolean,
  offset: number,
  limit: number | undefined,
  sortedRowIds: Ids,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table whose sorted Row Ids changed.

cellIdId | undefined

The Id of the Cell whose values were used for the sorting.

descendingboolean

Whether the sorting was in descending order.

offsetnumber

The number of Row Ids skipped.

limitnumber | undefined

The maximum number of Row Ids returned.

sortedRowIdsIds

The sorted Row Ids themselves.

returnsvoid

This has no return value.

A SortedRowIdsListener is provided when using the addSortedRowIdsListener method. See that method for specific examples.

When called, a SortedRowIdsListener is given a reference to the Store, the Id of the Table whose Row Ids sorting changed, the Cell Id being used to sort them, whether descending or not, and the offset and limit of the number of Ids returned, for pagination purposes. It also receives the sorted array of Ids itself, so that you can use them in the listener without the additional cost of an explicit call to getSortedRowIds.

Since

v2.0.0

HasRowListener

The HasRowListener type describes a function that is used to listen to a Row being added to or removed from the Store.

(
  store: Store,
  tableId: Id,
  rowId: Id,
  hasRow: boolean,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

rowIdId

The Id of the Row that changed.

hasRowboolean

Whether the Row now exists or not.

returnsvoid

This has no return value.

A HasRowListener is provided when using the addHasRowListener method. See that method for specific examples.

When called, a HasRowListener is given a reference to the Store, the Id of the Table that changed, and the Id of the Row that changed. It is also given a flag to indicate whether the Row now exists (having not done previously), or does not (having done so previously).

Since

v4.4.0

RowCountListener

The RowCountListener type describes a function that is used to listen to changes to the number of Row objects in a Table.

(
  store: Store,
  tableId: Id,
  count: number,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

countnumber

The number of Row objects in the Table.

returnsvoid

This has no return value.

A RowCountListener is provided when using the addRowCountListener method. See that method for specific examples.

When called, a RowCountListener is given a reference to the Store, the Id of the Table whose Row Ids changed, and the number of Row objects in the Table.

Since

v4.1.0

RowListener

The RowListener type describes a function that is used to listen to changes to a Row.

(
  store: Store,
  tableId: Id,
  rowId: Id,
  getCellChange: GetCellChange | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

rowIdId

The Id of the Row that changed.

getCellChangeGetCellChange | undefined

A function that returns information about any Cell's changes.

returnsvoid

This has no return value.

A RowListener is provided when using the addRowListener method. See that method for specific examples.

When called, a RowListener is given a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, and a GetCellChange function that can be used to query Cell values before and after the current transaction.

Note that if the listener was manually forced to be called (with the callListener method rather than due to a real change in the Store), the GetCellChange function will not be present.

Since

v1.0.0

CellIdsListener

The CellIdsListener type describes a function that is used to listen to changes to the Cell Ids in a Row.

(
  store: Store,
  tableId: Id,
  rowId: Id,
  getIdChanges: GetIdChanges | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

rowIdId

The Id of the Row that changed.

getIdChangesGetIdChanges | undefined

A function that returns information about the Id changes, since v3.3.

returnsvoid

This has no return value.

A CellIdsListener is provided when using the addCellIdsListener method. See that method for specific examples.

When called, a CellIdsListener is given a reference to the Store, the Id of the Table that changed, and the Id of the Row whose Cell Ids changed.

Since v3.3, the listener is also passed a GetIdChanges function that can be used to query which Ids changed during the transaction.

Since

v1.0.0

CellChange

The CellChange type describes a Cell's changes during a transaction.

[changed: boolean, oldCell: CellOrUndefined, newCell: CellOrUndefined]

This is returned by the GetCellChange function that is provided to every listener when called. This array contains the previous value of a Cell before the current transaction, the new value after it, and a convenience flag that indicates that the value has changed.

Since

v1.0.0

CellListener

The CellListener type describes a function that is used to listen to changes to a Cell.

(
  store: Store,
  tableId: Id,
  rowId: Id,
  cellId: Id,
  newCell: Cell,
  oldCell: Cell,
  getCellChange: GetCellChange | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

rowIdId

The Id of the Row that changed.

cellIdId

The Id of the Cell that changed.

newCellCell

The new value of the Cell that changed.

oldCellCell

The old value of the Cell that changed.

getCellChangeGetCellChange | undefined

A function that returns information about any Cell's changes.

returnsvoid

This has no return value.

A CellListener is provided when using the addCellListener method. See that method for specific examples.

When called, a CellListener is given a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, and the Id of Cell that changed. It is also given the new value of the Cell, the old value of the Cell, and a GetCellChange function that can be used to query Cell values before and after the current transaction.

Note that if the listener was manually forced to be called (with the callListener method rather than due to a real change in the Store), the GetCellChange function will not be present and the new and old values of the Cell will be the same.

Since

v1.0.0

GetCellChange

The GetCellChange type describes a function that returns information about any Cell's changes during a transaction.

(
  tableId: Id,
  rowId: Id,
  cellId: Id,
): CellChange
TypeDescription
tableIdId

The Id of the Table to inspect.

rowIdId

The Id of the Row to inspect.

cellIdId

The Id of the Cell to inspect.

returnsCellChange

A GetCellChange function is provided to every listener when called due the Store changing. The listener can then fetch the previous value of a Cell before the current transaction, the new value after it, and a convenience flag that indicates that the value has changed.

Since

v1.0.0

HasCellListener

The HasCellListener type describes a function that is used to listen to a Cell being added to or removed from the Store.

(
  store: Store,
  tableId: Id,
  rowId: Id,
  cellId: Id,
  hasCell: boolean,
): void
TypeDescription
storeStore

A reference to the Store that changed.

tableIdId

The Id of the Table that changed.

rowIdId

The Id of the Row that changed.

cellIdId

The Id of the Cell that changed.

hasCellboolean

Whether the Cell now exists or not.

returnsvoid

This has no return value.

A HasCellListener is provided when using the addHasCellListener method. See that method for specific examples.

When called, a HasCellListener is given a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, and the Id of Cell that changed. It is also given a flag to indicate whether the Cell now exists (having not done previously), or does not (having done so previously).

Since

v4.4.0

InvalidCellListener

The InvalidCellListener type describes a function that is used to listen to attempts to set invalid data to a Cell.

(
  store: Store,
  tableId: Id,
  rowId: Id,
  cellId: Id,
  invalidCells: any[],
): void
TypeDescription
storeStore

A reference to the Store that was being changed.

tableIdId

The Id of the Table that was being changed.

rowIdId

The Id of the Row that was being changed.

cellIdId

The Id of the Cell that was being changed.

invalidCellsany[]

An array of the values of the Cell that were invalid.

returnsvoid

This has no return value.

An InvalidCellListener is provided when using the addInvalidCellListener method. See that method for specific examples.

When called, an InvalidCellListener is given a reference to the Store, the Id of the Table, the Id of the Row, and the Id of Cell that was being attempted to be changed. It is also given the invalid value of the Cell, which could have been of absolutely any type. Since there could have been multiple failed attempts to set the Cell within a single transaction, this is an array containing each attempt, chronologically.

Since

v1.1.0

ValuesListener

The ValuesListener type describes a function that is used to listen to changes to all the Values in a Store.

(
  store: Store,
  getValueChange: GetValueChange | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

getValueChangeGetValueChange | undefined

A function that returns information about any Value's changes.

returnsvoid

This has no return value.

A ValuesListener is provided when using the addValuesListener method. See that method for specific examples.

When called, a ValuesListener is given a reference to the Store and a GetValueChange function that can be used to query Values before and after the current transaction.

Note that if the listener was manually forced to be called (with the callListener method rather than due to a real change in the Store), the GetValueChange function will not be present.

Since

v1.0.0

HasValuesListener

The HasValuesListener type describes a function that is used to listen to Values as a whole being added to or removed from the Store.

(
  store: Store,
  hasValues: boolean,
): void
TypeDescription
storeStore

A reference to the Store that changed.

hasValuesboolean

Whether Values now exist or not.

returnsvoid

This has no return value.

A HasValuesListener is provided when using the addHasValuesListener method. See that method for specific examples.

When called, a HasValuesListener is given a reference to the Store. It is also given a flag to indicate whether Values now exist (having not done previously), or do not (having done so previously).

Since

v4.4.0

GetValueChange

The GetValueChange type describes a function that returns information about any Value's changes during a transaction.

(valueId: Id): ValueChange
TypeDescription
valueIdId

The Id of the Value to inspect.

returnsValueChange

A GetValueChange function is provided to every listener when called due the Store changing. The listener can then fetch the previous value of a Value before the current transaction, the new value after it, and a convenience flag that indicates that the value has changed.

Since

v1.0.0

HasValueListener

The HasValueListener type describes a function that is used to listen to a Value being added to or removed from the Store.

(
  store: Store,
  valueId: Id,
  hasValue: boolean,
): void
TypeDescription
storeStore

A reference to the Store that changed.

valueIdId

The Id of the Value that changed.

hasValueboolean

Whether the Value now exists or not.

returnsvoid

This has no return value.

A HasValueListener is provided when using the addHasValueListener method. See that method for specific examples.

When called, a HasValueListener is given a reference to the Store and the Id of Value that changed. It is also given a flag to indicate whether the Value now exists (having not done previously), or does not (having done so previously).

Since

v4.4.0

InvalidValueListener

The InvalidValueListener type describes a function that is used to listen to attempts to set invalid data to a Value.

(
  store: Store,
  valueId: Id,
  invalidValues: any[],
): void
TypeDescription
storeStore

A reference to the Store that was being changed.

valueIdId

The Id of the Value that was being changed.

invalidValuesany[]

An array of the Values that were invalid.

returnsvoid

This has no return value.

An InvalidValueListener is provided when using the addInvalidValueListener method. See that method for specific examples.

When called, an InvalidValueListener is given a reference to the Store and the Id of Value that was being attempted to be changed. It is also given the invalid value of the Value, which could have been of absolutely any type. Since there could have been multiple failed attempts to set the Value within a single transaction, this is an array containing each attempt, chronologically.

Since

v3.0.0

ValueChange

The ValueChange type describes a Value's changes during a transaction.

[changed: boolean, oldValue: ValueOrUndefined, newValue: ValueOrUndefined]

This is returned by the GetValueChange function that is provided to every listener when called. This array contains the previous value of a Value before the current transaction, the new value after it, and a convenience flag that indicates that the value has changed.

Since

v1.0.0

ValueIdsListener

The ValueIdsListener type describes a function that is used to listen to changes to the Value Ids in a Store.

(
  store: Store,
  getIdChanges: GetIdChanges | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

getIdChangesGetIdChanges | undefined

A function that returns information about the Id changes, since v3.3.

returnsvoid

This has no return value.

A ValueIdsListener is provided when using the addValueIdsListener method. See that method for specific examples.

When called, a ValueIdsListener is given a reference to the Store.

Since v3.3, the listener is also passed a GetIdChanges function that can be used to query which Ids changed during the transaction.

Since

v1.0.0

ValueListener

The ValueListener type describes a function that is used to listen to changes to a Value.

(
  store: Store,
  valueId: Id,
  newValue: Value,
  oldValue: Value,
  getValueChange: GetValueChange | undefined,
): void
TypeDescription
storeStore

A reference to the Store that changed.

valueIdId

The Id of the Value that changed.

newValueValue

The new value of the Value that changed.

oldValueValue

The old value of the Value that changed.

getValueChangeGetValueChange | undefined

A function that returns information about any Value's changes.

returnsvoid

This has no return value.

A ValueListener is provided when using the addValueListener method. See that method for specific examples.

When called, a ValueListener is given a reference to the Store and the Id of Value that changed. It is also given the new value of the Value, the old value of the Value, and a GetValueChange function that can be used to query Values before and after the current transaction.

Note that if the listener was manually forced to be called (with the callListener method rather than due to a real change in the Store), the GetValueChange function will not be present and the new and old values of the Value will be the same.

Since

v3.0.0

GetIdChanges

The GetIdChanges type describes a function that returns information about the changes to a set of Ids during a transaction.

(): {[id: Id]: 1 | -1}
returns{[id: Id]: 1 | -1}

A GetIdChanges function is provided to every listener when called due Ids in the Store changing. It returns an object where each key is an Id that changed. The listener can then easily identify which Ids have been added (those with the value 1), and which have been removed (those with the value -1).

Since

v3.3.0

TransactionListener

The TransactionListener type describes a function that is used to listen to the completion of a transaction for the Store.

(store: Store): void
TypeDescription
storeStore

A reference to the Store that is completing a transaction.

returnsvoid

This has no return value.

A TransactionListener is provided when using the addWillFinishTransactionListener and addDidFinishTransactionListener methods. See those methods for specific examples.

Since v5.0, this listener is called with no arguments other than the Store. You can use the getTransactionChanges method and getTransactionLog method of the Store directly to get information about the changes made within the transaction.

Since

v1.0.0

Callback type aliases

This is the collection of callback type aliases within the store module. There are 9 callback type aliases in total.

TableCallback

The TableCallback type describes a function that takes a Table's Id and a callback to loop over each Row within it.

(
  tableId: Id,
  forEachRow: (rowCallback: RowCallback) => void,
): void
TypeDescription
tableIdId

The Id of the Table that the callback can operate on.

forEachRow(rowCallback: RowCallback) => void

A function that will let you iterate over the Row objects in this Table.

returnsvoid

This has no return value.

A TableCallback is provided when using the forEachTable method, so that you can do something based on every Table in the Store. See that method for specific examples.

Since

v1.0.0

TableCellCallback

The TableCellCallback type describes a function that takes a Cell's Id and the count of times it appears across a whole Table.

(
  cellId: Id,
  count: number,
): void
TypeDescription
cellIdId

The Id of the Cell that the callback can operate on.

countnumber

The number of times this Cell is used across a whole Table.

returnsvoid

This has no return value.

A TableCellCallback is provided when using the forEachTableCell method, so that you can do something based on every Cell used across a Table. See that method for specific examples.

Since

v1.0.0

RowCallback

The RowCallback type describes a function that takes a Row's Id and a callback to loop over each Cell within it.

(
  rowId: Id,
  forEachCell: (cellCallback: CellCallback) => void,
): void
TypeDescription
rowIdId

The Id of the Row that the callback can operate on.

forEachCell(cellCallback: CellCallback) => void
returnsvoid

This has no return value.

A RowCallback is provided when using the forEachRow method, so that you can do something based on every Row in a Table. See that method for specific examples.

Since

v1.0.0

CellCallback

The CellCallback type describes a function that takes a Cell's Id and its value.

(
  cellId: Id,
  cell: Cell,
): void
TypeDescription
cellIdId

The Id of the Cell that the callback can operate on.

cellCell

The value of the Cell.

returnsvoid

This has no return value.

A CellCallback is provided when using the forEachCell method, so that you can do something based on every Cell in a Row. See that method for specific examples.

Since

v1.0.0

GetCell

The GetCell type describes a function that takes a Id and returns the Cell value for a particular Row.

(cellId: Id): CellOrUndefined
TypeDescription
cellIdId

The Id of the Cell to fetch the value for.

returnsCellOrUndefined

A GetCell can be provided when setting definitions, as in the setMetricDefinition method of a Metrics object, or the setIndexDefinition method of an Indexes object. See those methods for specific examples.

Since

v1.0.0

MapCell

The MapCell type describes a function that takes an existing Cell value and returns another.

(cell: CellOrUndefined): Cell
TypeDescription
cellCellOrUndefined

The current value of the Cell to map to a new value.

returnsCell

A MapCell can be provided in the setCell method to map an existing value to a new one, such as when incrementing a number. See that method for specific examples.

Since

v1.0.0

MapValue

The MapValue type describes a function that takes an existing Value and returns another.

(value: ValueOrUndefined): Value
TypeDescription
valueValueOrUndefined

The current Value to map to a new Value.

returnsValue

A MapValue can be provided in the setValue method to map an existing Value to a new one, such as when incrementing a number. See that method for specific examples.

Since

v3.0.0

ValueCallback

The ValueCallback type describes a function that takes a Value's Id and its actual value.

(
  valueId: Id,
  value: Value,
): void
TypeDescription
valueIdId

The Id of the Value that the callback can operate on.

valueValue

The Value itself.

returnsvoid

This has no return value.

A ValueCallback is provided when using the forEachValue method, so that you can do something based on every Value in a Store. See that method for specific examples.

Since

v3.0.0

DoRollback

The DoRollback type describes a function that you can use to rollback the transaction if it did not complete to your satisfaction.

(store: Store): boolean
TypeDescription
storeStore

A reference to the Store that is completing a transaction.

returnsboolean

A DoRollback can be provided when calling the transaction method or the finishTransaction method. See those methods for specific examples.

Since v5.0, this function is called with the Store as a single argument. You can use the getTransactionChanges method and getTransactionLog method of the Store directly to decide whether to do the rollback.

Since

v1.0.0

Schema type aliases

This is the collection of schema type aliases within the store module. There are 10 schema type aliases in total.

TablesSchema

The TablesSchema type describes the tabular structure of a Store in terms of valid Table Ids and the types of Cell that can exist within them.

{[tableId: Id]: {[cellId: Id]: CellSchema}}

A TablesSchema comprises a JavaScript object describing each Table, in turn a nested JavaScript object containing information about each Cell and its CellSchema. It is provided to the setTablesSchema method.

Example

When applied to a Store, this TablesSchema only allows one Table called pets, in which each Row may contain a string species Cell, and is guaranteed to contain a boolean sold Cell that defaults to false.

import type {TablesSchema} from 'tinybase';

export const tableSchema: TablesSchema = {
  pets: {
    species: {type: 'string'},
    sold: {type: 'boolean', default: false},
  },
};
Since

v1.0.0

NoTablesSchema

The NoTablesSchema type is a TablesSchema-like type for when one has not been provided.

{[tableId: Id]: {[cellId: Id]: {type: "any"}}}
Since

v1.0.0

OptionalTablesSchema

The OptionalTablesSchema type is used by generic types that can optionally take a TablesSchema.

TablesSchema | NoTablesSchema
Since

v1.0.0

CellSchema

The CellSchema type describes what values are allowed for each Cell in a Table.

{
  type: "string";
  default?: string;
} | {
  type: "number";
  default?: number;
} | {
  type: "boolean";
  default?: boolean;
}

A CellSchema specifies the type of the Cell (string, boolean, or number), and what the default value can be when an explicit value is not specified.

If a default value is provided (and its type is correct), you can be certain that that Cell will always be present in a Row.

If the default value is not provided (or its type is incorrect), the Cell may be missing from the Row, but when present you can be guaranteed it is of the correct type.

Example

When applied to a Store, this CellSchema ensures a boolean Cell is always present, and defaults it to false.

import type {CellSchema} from 'tinybase';

export const requiredBoolean: CellSchema = {
  type: 'boolean',
  default: false,
};
Since

v1.0.0

ValuesSchema

The ValuesSchema type describes the keyed Values that can be set in a Store and their types.

{[valueId: Id]: ValueSchema}

A ValuesSchema comprises a JavaScript object describing each Value and its ValueSchema. It is provided to the setValuesSchema method.

Example

When applied to a Store, this ValuesSchema only allows one boolean Value called open, that defaults to false.

import type {ValuesSchema} from 'tinybase';

export const valuesSchema: ValuesSchema = {
  open: {type: 'boolean', default: false},
};
Since

v3.0.0

NoValuesSchema

The NoValuesSchema type is a ValuesSchema-like type for when one has not been provided.

{[valueId: Id]: {type: "any"}}
Since

v1.0.0

OptionalValuesSchema

The OptionalValuesSchema type is used by generic types that can optionally take a ValuesSchema.

ValuesSchema | NoValuesSchema
Since

v1.0.0

ValueSchema

The ValueSchema type describes what values are allowed for keyed Values in a Store.

{
  type: "string";
  default?: string;
} | {
  type: "number";
  default?: number;
} | {
  type: "boolean";
  default?: boolean;
}

A ValueSchema specifies the type of the Value (string, boolean, or number), and what the default value can be when an explicit value is not specified.

If a default value is provided (and its type is correct), you can be certain that the Value will always be present in a Store.

If the default value is not provided (or its type is incorrect), the Value may not be present in the Store, but when present you can be guaranteed it is of the correct type.

Example

When applied to a Store, this ValueSchema ensures a boolean Value is always present, and defaults it to false.

import type {ValueSchema} from 'tinybase';

export const requiredBoolean: ValueSchema = {
  type: 'boolean',
  default: false,
};
Since

v3.0.0

NoSchemas

The NoSchemas type is used as a default by generic types that can optionally take either or both of a TablesSchema and ValuesSchema.

[NoTablesSchema, NoValuesSchema]
Since

v1.0.0

OptionalSchemas

The OptionalSchemas type is used by generic types that can optionally take either or both of a TablesSchema and ValuesSchema.

[OptionalTablesSchema, OptionalValuesSchema]
Since

v1.0.0

Store type aliases

This is the collection of store type aliases within the store module. There are 9 store type aliases in total.

Tables

The Tables type is the data structure representing all of the data in a Store.

{[tableId: Id]: Table}

A Tables object is used when setting all of the tables together with the setTables method, and when getting them back out again with the getTables method. A Tables object is a regular JavaScript object containing individual Table objects, keyed by their Id.

Example
import type {Tables} from 'tinybase';

export const tables: Tables = {
  pets: {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat'},
  },
  species: {
    dog: {price: 5},
    cat: {price: 4},
  },
};
Since

v1.0.0

Table

The Table type is the data structure representing the data in a single table.

{[rowId: Id]: Row}

A Table is used when setting a table with the setTable method, and when getting it back out again with the getTable method. A Table object is a regular JavaScript object containing individual Row objects, keyed by their Id.

Example
import type {Table} from 'tinybase';

export const table: Table = {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat'},
};
Since

v1.0.0

Row

The Row type is the data structure representing the data in a single row.

{[cellId: Id]: Cell}

A Row is used when setting a row with the setRow method, and when getting it back out again with the getRow method. A Row object is a regular JavaScript object containing individual Cell objects, keyed by their Id.

Example
import type {Row} from 'tinybase';

export const row: Row = {species: 'dog', color: 'brown'};
Since

v1.0.0

Cell

The Cell type is the data structure representing the data in a single cell.

string | number | boolean

A Cell is used when setting a cell with the setCell method, and when getting it back out again with the getCell method. A Cell is a JavaScript string, number, or boolean.

Example
import type {Cell} from 'tinybase';

export const cell: Cell = 'dog';
Since

v1.0.0

CellOrUndefined

The CellOrUndefined type is a data structure representing the data in a single cell, or the value undefined.

Cell | undefined

This is used when describing a Cell that is present or that is not present, such as when it has been deleted, or when describing a previous state where the Cell value has since been added.

Since

v1.0.0

Values

The Values type is the data structure representing all the keyed values in a Store.

{[valueId: Id]: Value}

A Values object is used when setting values with the setValues method, and when getting them back out again with the getValues method. A Values object is a regular JavaScript object containing individual Value objects, keyed by their Id.

Example
import type {Values} from 'tinybase';

export const values: Values = {open: true, employees: 4};
Since

v3.0.0

Value

The Value type is the data structure representing the data in a single keyed value.

string | number | boolean

A Value is used when setting a value with the setValue method, and when getting it back out again with the getValue method. A Value is a JavaScript string, number, or boolean.

Example
import type {Value} from 'tinybase';

export const value: Value = 'dog';
Since

v3.0.0

ValueOrUndefined

The ValueOrUndefined type is a data structure representing the data in a single value, or the value undefined.

Value | undefined

This is used when describing a Value that is present or that is not present, such as when it has been deleted, or when describing a previous state where the Value has since been added.

Since

v3.0.0

Content

The Content type describes both the Tables and Values in a Store.

[Tables, Values]

It is an array of two objects, representing tabular and keyed value content.

Example

The following is a valid Content array:

[
  {
    "pets": {
      "fido": {
        "sold": false,
        "price": 4,
      },
      "felix": {
        "sold": true,
        "price": 5,
      },
    },
  },
  {
    open: true,
    employees: 3,
  },
]
Since

v5.0.0

Transaction type aliases

This is the collection of transaction type aliases within the store module. There are 13 transaction type aliases in total.

ChangedTableIds

The ChangedTableIds type describes the Table Ids that were added or removed during a transaction.

{[tableId: Id]: IdAddedOrRemoved}

It is available to the DoRollback function and to a TransactionListener function via the TransactionLog object.

It is a simple object that has Table Id as key, and an IdAddedOrRemoved number indicating whether the Table Id was added (1) or removed (-1).

Note that there will be no entry if the content of the Table itself changed. For that you should consult the ChangedRowIds, ChangedCellIds, or ChangedCells types.

Since

v4.0.0

ChangedRowIds

The ChangedRowIds type describes the Row Ids that were added or removed during a transaction.

{[tableId: Id]: {[rowId: Id]: IdAddedOrRemoved}}

It is available to the DoRollback function and to a TransactionListener function via the TransactionLog object.

It is a nested object that has Table Id as a top-level key, and then Row Id as an inner key. The values of the inner objects are IdAddedOrRemoved numbers indicating whether the Row Id was added (1) or removed (-1) to the given Table.

Note that there will be no entry if the content of the Row itself changed. For that you should consult the ChangedCellIds or ChangedCells types.

Since

v4.0.0

ChangedCellIds

The ChangedCellIds type describes the Cell Ids that were added or removed during a transaction.

{[tableId: Id]: {[rowId: Id]: {[cellId: Id]: IdAddedOrRemoved}}}

It is available to the DoRollback function and to a TransactionListener function via the TransactionLog object.

It is a nested object that has Table Id as a top-level key, and Row Id, and then CellId as inner keys. The values of the inner objects are IdAddedOrRemoved numbers indicating whether the Cell Id was added (1) or removed (-1) to the given Row.

Note that there will be no entry if the content of the Cell itself changed. For that you should consult the ChangedCells type.

Since

v4.0.0

ChangedCell

The ChangedCell type describes a Cell that has been changed during a transaction, primarily used so that you can indicate whether the transaction should be rolled back.

[oldCell: CellOrUndefined, newCell: CellOrUndefined]

It provides both the old and new Cell values in a two-part array. These are describing the state of the changed Cell in the Store at the start of the transaction, and by the end of the transaction.

Hence, an undefined value for the first item in the array means that the Cell was added during the transaction. An undefined value for the second item in the array means that the Cell was removed during the transaction. An array with two different Cell values indicates that it was changed. The two-part array will never contain two items of the same value (including two undefined values), even if, during the transaction, a Cell was changed to a different value and then changed back.

Since

v1.2.0

ChangedCells

The ChangedCells type describes the Cell values that have been changed during a transaction, primarily used so that you can indicate whether the transaction should be rolled back.

{[tableId: Id]: {[rowId: Id]: {[cellId: Id]: ChangedCell}}}

A ChangedCells object is provided to the doRollback callback when using the transaction method and the finishTransaction method. See those methods for specific examples.

This type is a nested structure of Table, Row, and Cell objects, much like the Tables object, but one which provides both the old and new Cell values in a two-part array. These are describing the state of each changed Cell in Store at the start of the transaction, and by the end of the transaction.

Hence, an undefined value for the first item in the array means that the Cell was added during the transaction. An undefined value for the second item in the array means that the Cell was removed during the transaction. An array with two different Cell values indicates that it was changed. The two-part array will never contain two items of the same value (including two undefined values), even if, during the transaction, a Cell was changed to a different value and then changed back.

Since

v1.2.0

InvalidCells

The InvalidCells type describes the invalid Cell values that have been attempted during a transaction, primarily used so that you can indicate whether the transaction should be rolled back.

{[tableId: Id]: {[rowId: Id]: {[cellId: Id]: any[]}}}

An InvalidCells object is provided to the doRollback callback when using the transaction method and the finishTransaction method. See those methods for specific examples.

This type is a nested structure of Table, Row, and Cell objects, much like the Tables object, but one for which Cell values are listed in array (much like the InvalidCellListener type) so that multiple failed attempts to change a Cell during the transaction are described.

Since

v1.2.0

ChangedValues

The ChangedValues type describes the Values that have been changed during a transaction, primarily used so that you can indicate whether the transaction should be rolled back.

{[valueId: Id]: ChangedValue}

A ChangedValues object is provided to the doRollback callback when using the transaction method and the finishTransaction method. See those methods for specific examples.

This type is an object containing the old and new Values in two-part arrays. These are describing the state of each changed Value in Store at the start of the transaction, and by the end of the transaction.

Hence, an undefined value for the first item in the array means that the Value was added during the transaction. An undefined value for the second item in the array means that the Value was removed during the transaction. An array with two different Values indicates that it was changed. The two-part array will never contain two items of the same value (including two undefined values), even if, during the transaction, a Value was changed to a different value and then changed back.

Since

v3.0.0

InvalidValues

The InvalidValues type describes the invalid Values that have been attempted during a transaction, primarily used so that you can indicate whether the transaction should be rolled back.

{[valueId: Id]: any[]}

An InvalidValues object is provided to the doRollback callback when using the transaction method and the finishTransaction method. See those methods for specific examples.

This type is an object containing each invalid Value's attempt listed in array (much like the InvalidValueListener type) so that multiple failed attempts to change a Value during the transaction are described.

Since

v3.0.0

ChangedValue

The ChangedValue type describes a Value that has been changed during a transaction, primarily used so that you can indicate whether the transaction should be rolled back.

[oldValue: ValueOrUndefined, newValue: ValueOrUndefined]

It provides both the the old and new Values in a two-part array. These describe the state of the changed Value in the Store at the start of the transaction, and by the end of the transaction.

Hence, an undefined value for the first item in the array means that the Value was added during the transaction. An undefined value for the second item in the array means that the Value was removed during the transaction. An array with two different Values indicates that it was changed. The two-part array will never contain two items of the same value (including two undefined values), even if, during the transaction, a Value was changed to a different value and then changed back.

Since

v3.0.0

ChangedValueIds

The ChangedValueIds type describes the Value Ids that were added or removed during a transaction.

{[valueId: Id]: IdAddedOrRemoved}

It is available to the DoRollback function and to a TransactionListener function via the TransactionLog object.

It is a simple object that has Value Id as key, and an IdAddedOrRemoved number indicating whether the Value Id was added (1) or removed (-1).

Note that there will be no entry if the content of the Value itself changed. For that you should consult the ChangedValues type.

Since

v4.0.0

Changes

The Changes type describes the net meaningful changes that were made to a Store during a transaction.

[changedTables: {[tableId: Id]: {[rowId: Id]: {[cellId: Id]: CellOrUndefined} | undefined} | undefined}, changedValues: {[valueId: Id]: ValueOrUndefined}, isChanges: 1]

This contains mostly equivalent information to a TransactionLog, but in a form that can be more efficiently parsed and serialized (for example in the case of synchronization between systems).

It is an array of two objects, representing tabular and keyed value changes. If the first item is an empty object, it means no tabular changes were made. If the second item is an empty object, it means no keyed value changes were made.

If not empty, the first object has an entry for each Table in a Store that has had a change within it. If the entry is null, it means that whole Table was deleted. Otherwise, the entry will be an object with an entry for each Row in that Table that had a change within it. In turn, if that entry is null, it means the Row was deleted. Otherwise, the entry will be an object with an entry for each Cell in that Row that had a change within it. If the entry is null, the Cell was deleted, otherwise it will contain the new value the Cell was changed to during the transaction.

If not empty, the second object has an entry for each Value in a Store that has had a change. If the entry is null, the Value was deleted, otherwise it will contain the new Value it was changed to during the transaction.

A third, required, item in the array is the digit 1, so that instances of Content and Changes types can be disambiguated.

Example

The following is a valid Changes array that conveys the following:

[
  {                     // changes to tabular data in the Store
    "pets": {             // this Table was changed
      "fido": null,         // this Row was deleted
      "felix": {            // this Row was changed
        "sold": true,         // this Cell was changed
        "price": null,        // this Cell was deleted
      },
    },
    "pendingSales": null, // this Table was deleted
  },
  {},                   // no changes to keyed value data in the Store
  1,                    // indicates that this is a Changes array
]
Since

v4.0.0

IdAddedOrRemoved

The IdAddedOrRemoved type describes a change made to an Id in either the tabular of keyed-value part of the Store.

1 | -1

This type is used in other types like ChangedTableIds, ChangedRowIds, ChangedCellIds, and ChangedValueIds.

It is a simple number: a 1 indicates that a given Id was added to the Store during a transaction, and a -1 indicates that it was removed.

Since

v4.0.0

TransactionLog

The TransactionLog type describes the changes that were made to a Store during a transaction in detail.

[cellsTouched: boolean, valuesTouched: boolean, changedCells: ChangedCells, invalidCells: InvalidCells, changedValues: ChangedValues, invalidValues: InvalidValues, changedTableIds: ChangedTableIds, changedRowIds: ChangedRowIds, changedCellIds: ChangedCellIds, changedValueIds: ChangedValueIds]

This contains equivalent information to a Changes object, but also information about what the previous state of the Store was. The changedCells and changedValues entries contain information about all changes to those parts of the Store, with their before and after values, for example.

cellsTouched and valuesTouched indicate whether Cell or Value data has been touched during the transaction. The two flags are intended as a hint about whether non-mutating listeners might be being called at the end of the transaction.

Here, 'touched' means that Cell or Value data has either been changed, or changed and then changed back to its original value during the transaction. The exception is a transaction that has been rolled back, for which the value of cellsTouched and valuesTouched in the listener will be false because all changes have been reverted.

In v5.0, this type changed from an object to an array, but still contains the same values.

See the documentation for the types of the inner objects for other details.

Since

v4.0.0

Development type aliases

This is the collection of development type aliases within the store module. There is only one type alias, StoreListenerStats.

StoreListenerStats

The StoreListenerStats type describes the number of listeners registered with the Store, and can be used for debugging purposes.

{
  hasTables: number;
  tables: number;
  tableIds: number;
  hasTable: number;
  table: number;
  tableCellIds: number;
  hasTableCell: number;
  rowCount: number;
  rowIds: number;
  sortedRowIds: number;
  hasRow: number;
  row: number;
  cellIds: number;
  hasCell: number;
  cell: number;
  invalidCell: number;
  hasValues: number;
  values: number;
  valueIds: number;
  hasValue: number;
  value: number;
  invalidValue: number;
  transaction: number;
}
TypeDescription
hasTablesnumber

The number of HasTablesListener functions registered with the Store, since v4.4.

tablesnumber

The number of TablesListener functions registered with the Store.

tableIdsnumber

The number of TableIdsListener functions registered with the Store.

hasTablenumber

The number of HasTableListener functions registered with the Store, since v4.4.

tablenumber

The number of TableListener functions registered with the Store.

tableCellIdsnumber

The number of TableCellIdsListener functions registered with the Store, since v3.3.

hasTableCellnumber

The number of HasTableCellListener functions registered with the Store, since v4.4.

rowCountnumber

The number of RowCountListener functions registered with the Store, since v4.1.0.

rowIdsnumber

The number of RowIdsListener functions registered with the Store.

sortedRowIdsnumber

The number of SortedRowIdsListener functions registered with the Store.

hasRownumber

The number of HasRowListener functions registered with the Store, since v4.4.

rownumber

The number of RowListener functions registered with the Store.

cellIdsnumber

The number of CellIdsListener functions registered with the Store.

hasCellnumber

The number of HasCellListener functions registered with the Store, since v4.4.

cellnumber

The number of CellListener functions registered with the Store.

invalidCellnumber

The number of InvalidCellListener functions registered with the Store.

hasValuesnumber

The number of HasValuesListener functions registered with the Store, since v4.4.

valuesnumber

The number of ValuesListener functions registered with the Store, since v3.0.

valueIdsnumber

The number of ValueIdsListener functions registered with the Store, since v3.0.

hasValuenumber

The number of HasValueListener functions registered with the Store, since v4.4.

valuenumber

The number of ValueListener functions registered with the Store, since v3.0.

invalidValuenumber

The number of InvalidValueListener functions registered with the Store, since v3.0.

transactionnumber

The number of TransactionListener functions registered with the Store.

The StoreListenerStats object contains a breakdown of the different types of listener. Totals include both mutator and non-mutator listeners. A StoreListenerStats object is returned from the getListenerStats method.

Since

v1.0.0

mergeable-store

The mergeable-store module contains the types, interfaces, and functions to work with MergeableStore objects, which provide merge and synchronization functionality.

The main entry point to this module is the createMergeableStore function, which returns a new MergeableStore, a subtype of Store that can be merged with another with deterministic results.

Please be aware that a lot of the types and methods exposed by this module are used internally within TinyBase itself (in particular the Synchronizer framework). They're documented here, but mostly for interest, and it is generally assumed that they won't be called directly by applications.

As an application developer, it's more likely that you will continue to use the main Store methods for reading, writing, and listening to data, and rely on Synchronizer instances to keep the data in step with other places.

Since

v5.0.0

Interfaces

There is one interface, MergeableStore, within the mergeable-store module.

MergeableStore

The MergeableStore type represents a Store that carries with it sufficient metadata to be able to be merged with another MergeableStore with deterministic results.

This is the key data type used when you need TinyBase data to be cleanly synchronized or merged with data elsewhere on the system, or on another system. It acts as a Conflict-Free Replicated Data Type (CRDT) which allows deterministic disambiguation of how changes to different instances should be merged.

Please be aware that a lot of the methods exposed by this interface are used internally within TinyBase itself (in particular the Synchronizer framework). They're documented here, but mostly for interest, and it is generally assumed that they won't be called directly by applications.

As an application developer, it's more likely that you will continue to use the main Store methods for reading, writing, and listening to data, and rely on Synchronizer instances to keep the data in step with other places.

One possible exceptions is the merge method, which can be used to simply merge two co-located MergeableStore instances together.

Example

This example shows very simple usage of the MergeableStore: whereby two are created, updated with different data, and then merged with one another.

import {createMergeableStore} from 'tinybase';

const localStore1 = createMergeableStore();
const localStore2 = createMergeableStore();

localStore1.setCell('pets', 'fido', 'color', 'brown');
localStore2.setCell('pets', 'felix', 'color', 'black');

localStore1.merge(localStore2);

console.log(localStore1.getContent());
// -> [{pets: {felix: {color: 'black'}, fido: {color: 'brown'}}}, {}]

console.log(localStore2.getContent());
// -> [{pets: {felix: {color: 'black'}, fido: {color: 'brown'}}}, {}]
Since

v5.0.0

Methods

These are the methods within the MergeableStore interface.

Getter methods

This is the collection of getter methods within the MergeableStore interface. There are 30 getter methods in total.

getTables

The getTables method returns a Tables object containing the entire tabular data of the Store.

getTables(): Tables
returnsTables

A Tables object containing the tabular data of the Store.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the Store itself.

Examples

This example retrieves the tabular data in a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}, species: {dog: {price: 5}}}

This example retrieves the Tables of an empty Store, returning an empty object.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTables());
// -> {}
Since

v1.0.0

getTablesJson

The getTablesJson method returns a string serialization of all of the Tables in the Store.

getTablesJson(): string
returnsstring

A string serialization of all of the Tables in the Store.

Examples

This example serializes the contents of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getTablesJson());
// -> '{"pets":{"fido":{"species":"dog"}}}'

This example serializes the contents of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTablesJson());
// -> '{}'
Since

v3.0.0

getTablesSchemaJson

The getTablesSchemaJson method returns a string serialization of the TablesSchema of the Store.

getTablesSchemaJson(): string
returnsstring

A string serialization of the TablesSchema of the Store.

If no TablesSchema has been set on the Store (or if it has been removed with the delTablesSchema method), then it will return the serialization of an empty object, {}.

Examples

This example serializes the TablesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {
    species: {type: 'string'},
    sold: {type: 'boolean'},
  },
});
console.log(store.getTablesSchemaJson());
// -> '{"pets":{"species":{"type":"string"},"sold":{"type":"boolean"}}}'

This example serializes the TablesSchema of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTablesSchemaJson());
// -> '{}'
Since

v3.0.0

hasTables

The hasTables method returns a boolean indicating whether any Table objects exist in the Store.

hasTables(): boolean
returnsboolean

Whether any Tables exist.

Example

This example shows simple existence checks.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.hasTables());
// -> false
store.setTables({pets: {fido: {species: 'dog'}}});
console.log(store.hasTables());
// -> true
Since

v1.0.0

hasTablesSchema

The hasTablesSchema method returns a boolean indicating whether the Store currently has a TablesSchema applied to it.

hasTablesSchema(): boolean
returnsboolean

Whether the Store has a TablesSchema applied to it.

Example

This example sets a TablesSchema and checks that it is present.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {
    price: {type: 'number'},
  },
});
console.log(store.hasTablesSchema());
// -> true

store.delTablesSchema();
console.log(store.hasTablesSchema());
// -> false
Since

v4.1.1

getTableIds

The getTableIds method returns the Ids of every Table in the Store.

getTableIds(): Ids
returnsIds

An array of the Ids of every Table in the Store.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Table Ids in a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
console.log(store.getTableIds());
// -> ['pets', 'species']

This example retrieves the Table Ids of an empty Store, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getTableIds());
// -> []
Since

v1.0.0

getTable

The getTable method returns an object containing the entire data of a single Table in the Store.

getTable(tableId: string): Table
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsTable

An object containing the entire data of the Table.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the Store itself.

Examples

This example retrieves the data in a single Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
console.log(store.getTable('pets'));
// -> {fido: {species: 'dog'}}

This example retrieves a Table that does not exist, returning an empty object.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getTable('employees'));
// -> {}
Since

v1.0.0

getTableCellIds

The getTableCellIds method returns the Ids of every Cell used across the whole Table.

getTableCellIds(tableId: string): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsIds

An array of the Ids of every Cell used across the whole Table.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Cell Ids used across a whole Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', legs: 4},
    cujo: {dangerous: true},
  },
});
console.log(store.getTableCellIds('pets'));
// -> ['species', 'color', 'legs', 'dangerous']

This example retrieves the Cell Ids used across a Table that does not exist, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getTableCellIds('species'));
// -> []
Since

v3.3.0

hasTable

The hasTable method returns a boolean indicating whether a given Table exists in the Store.

hasTable(tableId: string): boolean
TypeDescription
tableIdstring

The Id of a possible Table in the Store.

returnsboolean

Whether a Table with that Id exists.

Example

This example shows two simple Table existence checks.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.hasTable('pets'));
// -> true
console.log(store.hasTable('employees'));
// -> false
Since

v1.0.0

hasTableCell

The hasTableCell method returns a boolean indicating whether a given Cell exists anywhere in a Table, not just in a specific Row.

hasTableCell(
  tableId: string,
  cellId: string,
): boolean
TypeDescription
tableIdstring

The Id of a possible Table in the Store.

cellIdstring

The Id of a possible Cell in the Table.

returnsboolean

Whether a Cell with that Id exists anywhere in that Table.

Example

This example shows two simple Cell existence checks.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}, felix: {legs: 4}},
});
console.log(store.hasTableCell('pets', 'species'));
// -> true
console.log(store.hasTableCell('pets', 'legs'));
// -> true
console.log(store.hasTableCell('pets', 'color'));
// -> false
Since

v3.3.0

getRowIds

The getRowIds method returns the Ids of every Row in a given Table.

getRowIds(tableId: string): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsIds

An array of the Ids of every Row in the Table.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Row Ids in a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getRowIds('pets'));
// -> ['fido', 'felix']

This example retrieves the Row Ids of a Table that does not exist, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getRowIds('employees'));
// -> []
Since

v1.0.0

getSortedRowIds

The getSortedRowIds method returns the Ids of every Row in a given Table, sorted according to the values in a specified Cell.

getSortedRowIds(
  tableId: string,
  cellId?: string,
  descending?: boolean,
  offset?: number,
  limit?: number,
): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

cellId?string

The Id of the Cell whose values are used for the sorting, or undefined to sort by the Row Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of Row Ids to skip for pagination purposes, if any.

limit?number

The maximum number of Row Ids to return, or undefined for all.

returnsIds

An array of the sorted Ids of every Row in the Table.

The sorting of the rows is alphanumeric, and you can indicate whether it should be in descending order. The offset and limit parameters are used to paginate results, but default to 0 and undefined to return all available Row Ids if not specified.

Note that every call to this method will perform the sorting afresh - there is no caching of the results - and so you are advised to memoize the results yourself, especially when the Table is large. For a performant approach to tracking the sorted Row Ids when they change, use the addSortedRowIdsListener method.

If the Table does not exist, an empty array is returned.

Examples

This example retrieves sorted Row Ids in a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getSortedRowIds('pets', 'species'));
// -> ['felix', 'fido']

This example retrieves sorted Row Ids in a Table in reverse order.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'wolf'},
  },
});
console.log(store.getSortedRowIds('pets', 'species', true));
// -> ['cujo', 'fido', 'felix']

This example retrieves two pages of Row Ids in a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {price: 6},
    felix: {price: 5},
    mickey: {price: 2},
    tom: {price: 4},
    carnaby: {price: 3},
    lowly: {price: 1},
  },
});
console.log(store.getSortedRowIds('pets', 'price', false, 0, 2));
// -> ['lowly', 'mickey']
console.log(store.getSortedRowIds('pets', 'price', false, 2, 2));
// -> ['carnaby', 'tom']

This example retrieves Row Ids sorted by their own value, since the cellId parameter is undefined.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'wolf'},
  },
});
console.log(store.getSortedRowIds('pets'));
// -> ['cujo', 'felix', 'fido']

This example retrieves the sorted Row Ids of a Table that does not exist, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getSortedRowIds('employees'));
// -> []
Since

v2.0.0

getRow

The getRow method returns an object containing the entire data of a single Row in a given Table.

getRow(
  tableId: string,
  rowId: string,
): Row
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

returnsRow

An object containing the entire data of the Row.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the Store itself.

Examples

This example retrieves the data in a single Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getRow('pets', 'fido'));
// -> {species: 'dog'}

This example retrieves a Row that does not exist, returning an empty object.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getRow('pets', 'felix'));
// -> {}
Since

v1.0.0

getRowCount

The getRowCount method returns the count of the Row objects in a given Table.

getRowCount(tableId: string): number
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsnumber

The number of Row objects in the Table.

While this provides the same result as the length of Ids array returned from the getRowIds method, it is somewhat faster, and useful for efficient pagination.

Examples

This example retrieves the number of Row objects in the Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getRowCount('pets'));
// -> 2

This example retrieves the Row Ids of a Table that does not exist, returning zero.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getRowCount('employees'));
// -> 0
Since

v4.1.0

hasRow

The hasRow method returns a boolean indicating whether a given Row exists in the Store.

hasRow(
  tableId: string,
  rowId: string,
): boolean
TypeDescription
tableIdstring

The Id of a possible Table in the Store.

rowIdstring

The Id of a possible Row in the Table.

returnsboolean

Whether a Row with that Id exists in that Table.

Example

This example shows two simple Row existence checks.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.hasRow('pets', 'fido'));
// -> true
console.log(store.hasRow('pets', 'felix'));
// -> false
Since

v1.0.0

getCellIds

The getCellIds method returns the Ids of every Cell in a given Row in a given Table.

getCellIds(
  tableId: string,
  rowId: string,
): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

returnsIds

An array of the Ids of every Cell in the Row.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Cell Ids in a Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog', color: 'brown'},
  },
});
console.log(store.getCellIds('pets', 'fido'));
// -> ['species', 'color']

This example retrieves the Cell Ids of a Row that does not exist, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getCellIds('pets', 'felix'));
// -> []
Since

v1.0.0

getCell

The getCell method returns the value of a single Cell in a given Row, in a given Table.

getCell(
  tableId: string,
  rowId: string,
  cellId: string,
): CellOrUndefined
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

cellIdstring

The Id of the Cell in the Row.

returnsCellOrUndefined

The value of the Cell, or undefined.

Examples

This example retrieves a single Cell.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
console.log(store.getCell('pets', 'fido', 'species'));
// -> 'dog'

This example retrieves a Cell that does not exist, returning undefined.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.getCell('pets', 'fido', 'color'));
// -> undefined
Since

v1.0.0

hasCell

The hasCell method returns a boolean indicating whether a given Cell exists in a given Row in a given Table.

hasCell(
  tableId: string,
  rowId: string,
  cellId: string,
): boolean
TypeDescription
tableIdstring

The Id of a possible Table in the Store.

rowIdstring

The Id of a possible Row in the Table.

cellIdstring

The Id of a possible Cell in the Row.

returnsboolean

Whether a Cell with that Id exists in that Row in that Table.

Example

This example shows two simple Cell existence checks.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
console.log(store.hasCell('pets', 'fido', 'species'));
// -> true
console.log(store.hasCell('pets', 'fido', 'color'));
// -> false
Since

v1.0.0

getValues

The getValues method returns an object containing the entire set of keyed Values in the Store.

getValues(): Values
returnsValues

An object containing the set of keyed Values in the Store.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the Store itself.

Examples

This example retrieves the set of keyed Values in the Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValues());
// -> {open: true, employees: 3}

This example retrieves Values from a Store that has none, returning an empty object.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getValues());
// -> {}
Since

v3.0.0

getValuesJson

The getValuesJson method returns a string serialization of all of the keyed Values in the Store.

getValuesJson(): string
returnsstring

A string serialization of all of the Values in the Store.

Examples

This example serializes the keyed value contents of a Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
console.log(store.getValuesJson());
// -> '{"open":true}'

This example serializes the contents of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getValuesJson());
// -> '{}'
Since

v3.0.0

getValuesSchemaJson

The getValuesSchemaJson method returns a string serialization of the ValuesSchema of the Store.

getValuesSchemaJson(): string
returnsstring

A string serialization of the ValuesSchema of the Store.

If no ValuesSchema has been set on the Store (or if it has been removed with the delValuesSchema method), then it will return the serialization of an empty object, {}.

Examples

This example serializes the ValuesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({
  open: {type: 'boolean', default: false},
});
console.log(store.getValuesSchemaJson());
// -> '{"open":{"type":"boolean","default":false}}'

This example serializes the ValuesSchema of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getValuesSchemaJson());
// -> '{}'
Since

v3.0.0

hasValues

The hasValues method returns a boolean indicating whether any Values exist in the Store.

hasValues(): boolean
returnsboolean

Whether any Values exist.

Example

This example shows simple existence checks.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.hasValues());
// -> false
store.setValues({open: true});
console.log(store.hasValues());
// -> true
Since

v3.0.0

hasValuesSchema

The hasValuesSchema method returns a boolean indicating whether the Store currently has a ValuesSchema applied to it.

hasValuesSchema(): boolean
returnsboolean

Whether the Store has a ValuesSchema applied to it.

Example

This example sets a ValuesSchema and checks that it is present.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({open: {type: 'boolean'}});
console.log(store.hasValuesSchema());
// -> true

store.delValuesSchema();
console.log(store.hasValuesSchema());
// -> false
Since

v4.1.1

getValue

The getValue method returns a single keyed Value in the Store.

getValue(valueId: string): ValueOrUndefined
TypeDescription
valueIdstring

The Id of the Value in the Store.

returnsValueOrUndefined

The Value, or undefined.

Examples

This example retrieves a single Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValue('employees'));
// -> 3

This example retrieves a Value that does not exist, returning undefined.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValue('website'));
// -> undefined
Since

v3.0.0

getValueIds

The getValueIds method returns the Ids of every Value in a Store.

getValueIds(): Ids
returnsIds

An array of the Ids of every Value in the Store.

Note that this returns a copy of, rather than a reference, to the list of Ids, so changes made to the list are not made to the Store itself.

Examples

This example retrieves the Value Ids in a Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValueIds());
// -> ['open', 'employees']

This example retrieves the Value Ids of a Store that has had none set, returning an empty array.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getValueIds());
// -> []
Since

v3.0.0

hasValue

The hasValue method returns a boolean indicating whether a given Value exists in the Store.

hasValue(valueId: string): boolean
TypeDescription
valueIdstring

The Id of a possible Value in the Store.

returnsboolean

Whether a Value with that Id exists in the Store.

Example

This example shows two simple Value existence checks.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
console.log(store.hasValue('open'));
// -> true
console.log(store.hasValue('employees'));
// -> false
Since

v3.0.0

getContent

The getContent method returns a Tables object and a Values object in an array, representing the entire content of the Store.

getContent(): Content
returnsContent

An array of a Tables object and a Values object.

Note that this returns a copy of, rather than a reference to the underlying data, so changes made to the returned objects are not made to the Store itself.

Examples

This example retrieves the content of a Store.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog'}}})
  .setValues({open: true, employees: 3});
console.log(store.getContent());
// -> [{pets: {fido: {species: 'dog'}}}, {open: true, employees: 3}]

This example retrieves the Tables and Values of an empty Store, returning empty objects.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getContent());
// -> [{}, {}]
Since

v4.0.0

getJson

The getJson method returns a string serialization of all the Store content: both the Tables and the keyed Values.

getJson(): string
returnsstring

A string serialization of the Tables and Values in the Store.

From v3.0 onwards, the serialization is of an array with two entries. The first is the Tables object, the second the Values. In previous versions (before the existence of the Values data structure), it was a sole object of Tables.

Examples

This example serializes the tabular and keyed value contents of a Store.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog'}}})
  .setValues({open: true});
console.log(store.getJson());
// -> '[{"pets":{"fido":{"species":"dog"}}},{"open":true}]'

This example serializes the contents of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getJson());
// -> '[{},{}]'
Since

v1.0.0

getMergeableContent

The getMergeableContent method returns the full content of a MergeableStore, together with the metadata required to make it mergeable with another.

getMergeableContent(): MergeableContent
returnsMergeableContent

A MergeableContent object for the full content of the MergeableStore.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates a MergeableStore, sets some data, and then accesses the content and metadata required to make it mergeable.

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1');

store.setCell('pets', 'fido', 'color', 'brown');

console.log(store.getMergeableContent());
// ->
[
  [
    {
      pets: [
        {
          fido: [
            {color: ['brown', 'Nn1JUF-----FnHIC', 923684530]},
            '',
            851131566,
          ],
        },
        '',
        518810247,
      ],
    },
    '',
    784336119,
  ],
  [{}, '', 0],
];
Since

v5.0.0

getSchemaJson

The getSchemaJson method returns a string serialization of both the TablesSchema and ValuesSchema of the Store.

getSchemaJson(): string
returnsstring

A string serialization of the TablesSchema and ValuesSchema of the Store.

From v3.0 onwards, the serialization is of an array with two entries. The first is the TablesSchema object, the second the ValuesSchema. In previous versions (before the existence of the ValuesSchema data structure), it was a sole object of TablesSchema.

Examples

This example serializes the TablesSchema and ValuesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore()
  .setTablesSchema({
    pets: {
      price: {type: 'number'},
    },
  })
  .setValuesSchema({
    open: {type: 'boolean'},
  });
console.log(store.getSchemaJson());
// -> '[{"pets":{"price":{"type":"number"}}},{"open":{"type":"boolean"}}]'

This example serializes the TablesSchema and ValuesSchema of an empty Store.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.getSchemaJson());
// -> '[{},{}]'
Since

v1.0.0

Setter methods

This is the collection of setter methods within the MergeableStore interface. There are 21 setter methods in total.

setTables

The setTables method takes an object and sets the entire tabular data of the Store.

setTables(tables: Tables): this
TypeDescription
tablesTables

The data of the Store to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Tables type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Tables object is valid, any data that was already present in the Store will be completely overwritten. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the tabular data of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}, species: {dog: {price: 5}}}

This example attempts to set the tabular data of an existing Store with partly invalid, and then completely invalid, Tables objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setTables({pets: {felix: {species: 'cat', bug: []}}});
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

store.setTables({meaning: 42});
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
Since

v1.0.0

setTablesJson

The setTablesJson method takes a string serialization of all of the Tables in the Store and attempts to update them to that.

setTablesJson(tablesJson: string): this
TypeDescription
tablesJsonstring

A string serialization of all of the Tables in the Store.

returnsthis

A reference to the Store.

If the JSON cannot be parsed, this will fail silently. If it can be parsed, it will then be subject to the same validation rules as the setTables method (according to the Tables type, and matching any TablesSchema associated with the Store).

Examples

This example sets the tabular contents of a Store from a serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setTablesJson('{"pets": {"fido": {"species": "dog"}}}');
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example attempts to set the tabular contents of a Store from an invalid serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setTablesJson('{"pets": {"fido": {');
console.log(store.getTables());
// -> {}
Since

v3.0.0

setTablesSchema

The setTablesSchema method lets you specify the TablesSchema of the tabular part of the Store.

setTablesSchema(tablesSchema: TablesSchema): this
TypeDescription
tablesSchemaTablesSchema

The TablesSchema to be set for the Store.

returnsthis

A reference to the Store.

Note that this may result in a change to data in the Store, as defaults are applied or as invalid Table, Row, or Cell objects are removed. These changes will fire any listeners to that data, as expected.

When no longer needed, you can also completely remove an existing TablesSchema with the delTablesSchema method.

Example

This example sets the TablesSchema of a Store after it has been created.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {
    species: {type: 'string'},
    sold: {type: 'boolean', default: false},
  },
});
store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});

console.log(store.getTables());
// -> {pets: {0: {species: 'dog', sold: false}}}
Since

v3.0.0

setTable

The setTable method takes an object and sets the entire data of a single Table in the Store.

setTable(
  tableId: string,
  table: Table,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

tableTable

The data of a single Table to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Table type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Table object is valid, any data that was already present in the Store for that Table will be completely overwritten. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the data of a single Table.

import {createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

This example attempts to set the data of an existing Store with partly invalid, and then completely invalid, Table objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setTable('pets', {felix: {species: 'cat', bug: []}});
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

store.setTable('pets', {meaning: 42});
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
Since

v1.0.0

setRow

The setRow method takes an object and sets the entire data of a single Row in the Store.

setRow(
  tableId: string,
  rowId: string,
  row: Row,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

rowRow

The data of a single Row to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Row type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Row object is valid, any data that was already present in the Store for that Row will be completely overwritten. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the data of a single Row.

import {createStore} from 'tinybase';

const store = createStore().setRow('pets', 'fido', {species: 'dog'});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example attempts to set the data of an existing Store with partly invalid, and then completely invalid, Row objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setRow('pets', 'fido', {color: 'brown', bug: []});
console.log(store.getTables());
// -> {pets: {fido: {color: 'brown'}}}

store.setRow('pets', 'fido', 42);
console.log(store.getTables());
// -> {pets: {fido: {color: 'brown'}}}
Since

v1.0.0

addRow

The addRow method takes an object and creates a new Row in the Store, returning the unique Id assigned to it.

addRow(
  tableId: string,
  row: Row,
  reuseRowIds?: boolean,
): undefined | string
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowRow

The data of a single Row to be added.

reuseRowIds?boolean

Whether Ids should be recycled from previously deleted Row objects, defaulting to true.

returnsundefined | string

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Row type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Row object is valid, a new Row will be created. If the object is completely invalid, no change will be made to the Store and the method will return undefined.

You should not guarantee the form of the unique Id that is generated when a Row is added to the Table. However it is likely to be a string representation of an increasing integer.

The reuseRowIds parameter defaults to true, which means that if you delete a Row and then add another, the Id will be re-used - unless you delete the entire Table, in which case all Row Ids will reset. Otherwise, if you specify reuseRowIds to be false, then the Id will be a monotonically increasing string representation of an increasing integer, regardless of any you may have previously deleted.

Examples

This example adds a single Row.

import {createStore} from 'tinybase';

const store = createStore();
console.log(store.addRow('pets', {species: 'dog'}));
// -> '0'
console.log(store.getTables());
// -> {pets: {'0': {species: 'dog'}}}

This example attempts to add Rows to an existing Store with partly invalid, and then completely invalid, Row objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {'0': {species: 'dog'}}});

console.log(store.addRow('pets', {species: 'cat', bug: []}));
// -> '1'
console.log(store.getTables());
// -> {pets: {'0': {species: 'dog'}, '1': {species: 'cat'}}}

console.log(store.addRow('pets', 42));
// -> undefined
console.log(store.getTables());
// -> {pets: {'0': {species: 'dog'}, '1': {species: 'cat'}}}
Since

v1.0.0

setPartialRow

The setPartialRow method takes an object and sets partial data of a single Row in the Store, leaving other Cell values unaffected.

setPartialRow(
  tableId: string,
  rowId: string,
  partialRow: Row,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

partialRowRow

The partial data of a single Row to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Row type, or because, when combined with the current Row data, it does not match a TablesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Row object is valid, it will be merged with the data that was already present in the Store. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets some of the data of a single Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
store.setPartialRow('pets', 'fido', {color: 'walnut', visits: 1});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'walnut', visits: 1}}}

This example attempts to set some of the data of an existing Store with partly invalid, and then completely invalid, Row objects.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setPartialRow('pets', 'fido', {color: 'brown', bug: []});
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}

store.setPartialRow('pets', 'fido', 42);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
Since

v1.0.0

setCell

The setCell method sets the value of a single Cell in the Store.

setCell(
  tableId: string,
  rowId: string,
  cellId: string,
  cell: Cell | MapCell,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

cellIdstring

The Id of the Cell in the Row.

cellCell | MapCell

The value of the Cell to be set, or a MapCell function to update it.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, or Id changes resulting from it.

If the Cell value is invalid (either because of its type, or because it does not match a TablesSchema associated with the Store), will be ignored silently.

As well as string, number, or boolean Cell types, this method can also take a MapCell function that takes the current Cell value as a parameter and maps it. This is useful if you want to efficiently increment a value without fetching it first, for example.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the value of a single Cell.

import {createStore} from 'tinybase';

const store = createStore().setCell('pets', 'fido', 'species', 'dog');
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example sets the data of a single Cell by mapping the existing value.

import {createStore} from 'tinybase';

const increment = (cell) => cell + 1;
const store = createStore().setTables({pets: {fido: {visits: 1}}});

store.setCell('pets', 'fido', 'visits', increment);
console.log(store.getCell('pets', 'fido', 'visits'));
// -> 2

This example attempts to set the data of an existing Store with an invalid Cell value.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.setCell('pets', 'fido', 'bug', []);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}
Since

v1.0.0

setPartialValues

The setPartialValues method takes an object and sets its Values in the Store, but leaving existing Values unaffected.

setPartialValues(partialValues: Values): this
TypeDescription
partialValuesValues

The Values to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Values or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Values type, or because, when combined with the current Values data, it does not match a ValuesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Values object is valid, it will be merged with the data that was already present in the Store. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets some of the keyed value data in a Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
store.setPartialValues({employees: 3});
console.log(store.getValues());
// -> {open: true, employees: 3}

This example attempts to set some of the data of an existing Store with partly invalid, and then completely invalid, Values objects.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});

store.setPartialValues({employees: 3, bug: []});
console.log(store.getValues());
// -> {open: true, employees: 3}

store.setPartialValues(42);
console.log(store.getValues());
// -> {open: true, employees: 3}
Since

v3.0.0

setValues

The setValues method takes an object and sets all the Values in the Store.

setValues(values: Values): this
TypeDescription
valuesValues

The Values object to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Value or Id changes resulting from it.

Any part of the provided object that is invalid (either according to the Values type, or because it does not match a ValuesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Values object is valid, any data that was already present in the Store for that Values will be completely overwritten. If the object is completely invalid, no change will be made to the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the Values of a Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
console.log(store.getValues());
// -> {open: true, employees: 3}

This example attempts to set the data of an existing Store with partly invalid, and then completely invalid, Values objects.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});

store.setValues({employees: 3, bug: []});
console.log(store.getValues());
// -> {employees: 3}

store.setValues(42);
console.log(store.getValues());
// -> {employees: 3}
Since

v3.0.0

setValuesJson

The setValuesJson method takes a string serialization of all of the Values in the Store and attempts to update them to those values.

setValuesJson(valuesJson: string): this
TypeDescription
valuesJsonstring

A string serialization of all of the Values in the Store.

returnsthis

A reference to the Store.

If the JSON cannot be parsed, this will fail silently. If it can be parsed, it will then be subject to the same validation rules as the setValues method (according to the Values type, and matching any ValuesSchema associated with the Store).

Examples

This example sets the keyed value contents of a Store from a serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setValuesJson('{"open": true}');
console.log(store.getValues());
// -> {open: true}

This example attempts to set the keyed value contents of a Store from an invalid serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setValuesJson('{"open": false');
console.log(store.getValues());
// -> {}
Since

v3.0.0

setValuesSchema

The setValuesSchema method lets you specify the ValuesSchema of the keyed Values part of the Store.

setValuesSchema(valuesSchema: ValuesSchema): this
TypeDescription
valuesSchemaValuesSchema

The ValuesSchema to be set for the Store.

returnsthis

A reference to the Store.

Note that this may result in a change to data in the Store, as defaults are applied or as invalid Values are removed. These changes will fire any listeners to that data, as expected.

When no longer needed, you can also completely remove an existing ValuesSchema with the delValuesSchema method.

Example

This example sets the ValuesSchema of a Store after it has been created.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({
  open: {type: 'boolean', default: false},
});
store.setValue('open', 'maybe');

console.log(store.getValues());
// -> {open: false}
Since

v3.0.0

setValue

The setValue method sets a single keyed Value in the Store.

setValue(
  valueId: string,
  value: Value | MapValue,
): this
TypeDescription
valueIdstring

The Id of the Value in the Store.

valueValue | MapValue

The Value to be set, or a MapValue function to update it.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Value, or Id changes resulting from it.

If the Value is invalid (either because of its type, or because it does not match a ValuesSchema associated with the Store), will be ignored silently.

As well as string, number, or boolean Value types, this method can also take a MapValue function that takes the current Value as a parameter and maps it. This is useful if you want to efficiently increment a value without fetching it first, for example.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets a single Value.

import {createStore} from 'tinybase';

const store = createStore().setValue('open', true);
console.log(store.getValues());
// -> {open: true}

This example sets the data of a single Value by mapping the existing Value.

import {createStore} from 'tinybase';

const increment = (value) => value + 1;
const store = createStore().setValues({employees: 3});

store.setValue('employees', increment);
console.log(store.getValue('employees'));
// -> 4

This example attempts to set an invalid Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({employees: 3});

store.setValue('bug', []);
console.log(store.getValues());
// -> {employees: 3}
Since

v3.0.0

applyChanges

The applyChanges method applies a set of Changes to the Store.

applyChanges(changes: Changes): this
TypeDescription
changesChanges

The Changes to apply to the Store.

returnsthis

A reference to the Store.

This method will take a Changes object (which is available at the end of a transaction) and apply it to a Store. The most likely need to do this is to take the changes made during the transaction of one Store, and apply it to the content of another Store - such as when persisting and synchronizing data.

Any part of the provided Changes object are invalid (either because of its type, or because it does not match the schemas associated with the Store) will be ignored silently.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Prior to v5.0, this method was named setTransactionChanges.

Example

This example applies a Changes object that sets a Cell and removes a Value.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store.applyChanges([{pets: {fido: {color: 'black'}}}, {open: null}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'black'}}}
console.log(store.getValues());
// -> {}
Since

v5.0.0

applyMergeableChanges

The applyMergeableChanges method applies a set of mergeable changes or content to the MergeableStore.

applyMergeableChanges(mergeableChanges: MergeableContent | MergeableChanges): MergeableStore
TypeDescription
mergeableChangesMergeableContent | MergeableChanges

The MergeableChanges or MergeableContent to apply to the MergeableStore.

returnsMergeableStore

A reference to the MergeableStore.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example applies a MergeableChanges object that sets a Cell and removes a Value.

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1')
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store.applyMergeableChanges([
  [{pets: [{fido: [{color: ['black', 'Nn1JUF----2FnHIC']}]}]}],
  [{open: [null, 'Nn1JUF----3FnHIC']}],
  1,
]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'black'}}}
console.log(store.getValues());
// -> {}
Since

v5.0.0

merge

The merge method is a convenience method that applies the mergeable content from two MergeableStores to each other in order to bring them to the same state.

merge(mergeableStore: MergeableStore): MergeableStore
TypeDescription
mergeableStoreMergeableStore

A reference to the other MergeableStore to merge with this one.

returnsMergeableStore

A reference to this MergeableStore.

This method is symmetrical: applying store1 to store2 will have exactly the same effect as applying store2 to store1.

Example

This example merges two MergeableStore objects together. Note how the final part of the timestamps on each Cell give you a clue that the data comes from changes made to different MergeableStore objects.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setTables({pets: {fido: {species: 'dog', color: 'brown'}}});

const store2 = createMergeableStore('store2');
store2.setTables({pets: {felix: {species: 'cat', color: 'tan'}}});

store1.merge(store2);

console.log(store1.getContent());
// ->
[
  {
    pets: {
      felix: {color: 'tan', species: 'cat'},
      fido: {color: 'brown', species: 'dog'},
    },
  },
  {},
];

console.log(store2.getContent());
// ->
[
  {
    pets: {
      felix: {color: 'tan', species: 'cat'},
      fido: {color: 'brown', species: 'dog'},
    },
  },
  {},
];
console.log(store2.getMergeableContent());
// ->
[
  [
    {
      pets: [
        {
          felix: [
            {
              color: ['tan', 'Nn1JUF----0CnH-J', 2576658292],
              species: ['cat', 'Nn1JUF-----CnH-J', 3409607562],
            },
            '',
            4146239216,
          ],
          fido: [
            {
              color: ['brown', 'Nn1JUF----0FnHIC', 1240535355],
              species: ['dog', 'Nn1JUF-----FnHIC', 290599168],
            },
            '',
            3989065420,
          ],
        },
        '',
        4155188296,
      ],
    },
    '',
    972931118,
  ],
  [{}, '', 0],
];
Since

v5.0.0

setContent

The setContent method takes an array of two objects and sets the entire data of the Store.

setContent(content: Content): this
TypeDescription
contentContent

An array containing the tabular and keyed-value data of the Store to be set.

returnsthis

A reference to the Store.

This method will cause listeners to be called for any Table, Row, Cell, Value, or Id changes resulting from it.

Any part of the provided objects that are invalid (either according to the Tables or Values type, or because it does not match a TablesSchema or ValuesSchema associated with the Store), will be ignored silently.

Assuming that at least some of the provided Tables object or Values object is valid, any data that was already present in that part of the Store will be completely overwritten. If either object is completely invalid, no change will be made to the corresponding part of the Store.

The method returns a reference to the Store so that subsequent operations can be chained in a fluent style.

Examples

This example sets the data of a Store.

import {createStore} from 'tinybase';

const store = createStore().setContent([
  {pets: {fido: {species: 'dog'}}},
  {open: true, employees: 3},
]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store.getValues());
// -> {open: true, employees: 3}

This example attempts to set the data of an existing Store with partly invalid, and then completely invalid objects.

import {createStore} from 'tinybase';

const store = createStore().setContent([
  {pets: {fido: {species: 'dog'}}},
  {open: true, employees: 3},
]);

store.setContent([{pets: {felix: {species: 'cat', bug: []}}}, '']);
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
console.log(store.getValues());
// -> {open: true, employees: 3}

store.setContent([{meaning: 42}]);
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
Since

v4.0.0

setDefaultContent

The setDefaultContent method sets initial content of a MergeableStore.

setDefaultContent(content: Content): MergeableStore
TypeDescription
contentContent

An array containing the tabular and keyed-value data to be set.

returnsMergeableStore

A reference to the MergeableStore.

This differs from the setMergeableContent method in that all of the metadata is initialized with a empty HLC timestamp - meaning that any changes applied to it will 'win', yet ensuring that at least default, initial data exists.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates a new MergeableStore with default data, and demonstrates that it is overwritten with another MergeableStore's data on merge, even if the other MergeableStore was provisioned earlier.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setValues({employees: 3});

const store2 = createMergeableStore('store2');
store2.setDefaultContent([{}, {employees: 4}]);
console.log(store2.getMergeableContent());
// -> [[{}, "", 0], [{"employees": [4, "", 2414055963]}, "", 3035768673]]

store2.merge(store1);
console.log(store2.getContent());
// -> [{}, {employees: 3}]
Since

v5.0.0

setJson

The setJson method takes a string serialization of all of the Tables and Values in the Store and attempts to update them to those values.

setJson(tablesAndValuesJson: string): this
TypeDescription
tablesAndValuesJsonstring

A string serialization of all of the Tables and Values in the Store.

returnsthis

A reference to the Store.

From v3.0 onwards, the serialization should be of an array with two entries. The first is the Tables object, the second the Values. In previous versions (before the existence of the Values data structure), it was a sole object of Tables. For backwards compatibility, if a serialization of a single object is provided, it will be treated as the Tables type.

If the JSON cannot be parsed, this will fail silently. If it can be parsed, it will then be subject to the same validation rules as the setTables method (according to the Tables type, and matching any TablesSchema associated with the Store), and the setValues method (according to the Values type, and matching any ValuesSchema associated with the Store).

Examples

This example sets the tabular and keyed value contents of a Store from a serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setJson('[{"pets": {"fido": {"species": "dog"}}}, {"open": true}]');
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store.getValues());
// -> {open: true}

This example sets the tabular contents of a Store from a legacy single-object serialization (compatible with v2.x and earlier).

import {createStore} from 'tinybase';

const store = createStore();
store.setJson('{"pets": {"fido": {"species": "dog"}}}');
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store.getValues());
// -> {}

This example attempts to set both the tabular and keyed value contents of a Store from an invalid serialization.

import {createStore} from 'tinybase';

const store = createStore();
store.setValuesJson('[{"pets": {"fido": {"species": "do');
console.log(store.getTables());
// -> {}
console.log(store.getValues());
// -> {}
Since

v1.0.0

setMergeableContent

The setMergeableContent method sets the full content of a MergeableStore, together with the metadata required to make it mergeable with another.

setMergeableContent(mergeableContent: MergeableContent): MergeableStore
TypeDescription
mergeableContentMergeableContent

The full content and metadata of a MergeableStore.

returnsMergeableStore

A reference to the MergeableStore.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates a new MergeableStore and initializes it with the content and metadata from another.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setValues({employees: 3});
console.log(store1.getMergeableContent());
// ->
[
  [{}, '', 0],
  [{employees: [3, 'Nn1JUF-----FnHIC', 1940815977]}, '', 1260895905],
];

const store2 = createMergeableStore('store2');
store2.setMergeableContent(store1.getMergeableContent());
console.log(store2.getMergeableContent());
// ->
[
  [{}, '', 0],
  [{employees: [3, 'Nn1JUF-----FnHIC', 1940815977]}, '', 1260895905],
];
Since

v5.0.0

setSchema

The setSchema method lets you specify the TablesSchema and ValuesSchema of the Store.

setSchema(
  tablesSchema: TablesSchema,
  valuesSchema?: ValuesSchema,
): this
TypeDescription
tablesSchemaTablesSchema

The TablesSchema to be set for the Store.

valuesSchema?ValuesSchema

The ValuesSchema to be set for the Store.

returnsthis

A reference to the Store.

Note that this may result in a change to data in the Store, as defaults are applied or as invalid Table, Row, Cell, or Value objects are removed. These changes will fire any listeners to that data, as expected.

From v3.0 onwards, this method takes two arguments. The first is the TablesSchema object, the second the ValuesSchema. In previous versions (before the existence of the ValuesSchema data structure), only the first was present. For backwards compatibility the new second parameter is optional.

Examples

This example sets the TablesSchema and ValuesSchema of a Store after it has been created.

import {createStore} from 'tinybase';

const store = createStore().setSchema(
  {
    pets: {
      species: {type: 'string'},
      sold: {type: 'boolean', default: false},
    },
  },
  {open: {type: 'boolean', default: false}},
);
store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});
store.setValue('open', 'maybe');

console.log(store.getTables());
// -> {pets: {0: {species: 'dog', sold: false}}}
console.log(store.getValues());
// -> {open: false}

This example sets just the TablesSchema of a Store after it has been created.

import {createStore} from 'tinybase';

const store = createStore().setSchema({
  pets: {
    species: {type: 'string'},
    sold: {type: 'boolean', default: false},
  },
});
store.addRow('pets', {species: 'dog', color: 'brown', sold: 'maybe'});

console.log(store.getTables());
// -> {pets: {0: {species: 'dog', sold: false}}}
Since

v1.0.0

Listener methods

This is the collection of listener methods within the MergeableStore interface. There are 27 listener methods in total.

addHasTablesListener

The addHasTablesListener method registers a listener function with the Store that will be called when Tables as a whole are added to or removed from the Store.

addHasTablesListener(
  listener: HasTablesListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
listenerHasTablesListener<MergeableStore>

The function that will be called whenever Tables as a whole are added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasTablesListener function, and will be called with a reference to the Store. It is also given a flag to indicate whether Tables now exist (having not done previously), or do not (having done so previously).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to Tables being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTablesListener((store, hasTables) => {
  console.log('Tables ' + (hasTables ? 'added' : 'removed'));
});

store.delTables();
// -> 'Tables removed'

store.setTables({species: {dog: {price: 5}}});
// -> 'Tables added'

store.delListener(listenerId);

This example registers a listener that responds to Tables being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore();
const listenerId = store.addHasTablesListener(
  (store, hasTables) => store.setValue('hasTables', hasTables),
  true,
);

store.setTables({species: {dog: {price: 5}}});
console.log(store.getValues());
// -> {hasTables: true}

store.delListener(listenerId);
Since

v4.4.0

addTablesListener

The addTablesListener method registers a listener function with the Store that will be called whenever data in the Store changes.

addTablesListener(
  listener: TablesListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
listenerTablesListener<MergeableStore>

The function that will be called whenever data in the Store changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a TablesListener function, and will be called with a reference to the Store and a GetCellChange function in case you need to inspect any changes that occurred.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to the whole Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTablesListener((store, getCellChange) => {
  console.log('Tables changed');
  console.log(getCellChange('pets', 'fido', 'color'));
});

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Tables changed'
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to the whole Store, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTablesListener(
  (store) => store.setCell('meta', 'update', 'store', true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {store: true}}

store.delListener(listenerId);
Since

v1.0.0

addTableIdsListener

The addTableIdsListener method registers a listener function with the Store that will be called whenever the Table Ids in the Store change.

addTableIdsListener(
  listener: TableIdsListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
listenerTableIdsListener<MergeableStore>

The function that will be called whenever the Table Ids in the Store change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a TableIdsListener function, and will be called with a reference to the Store.

By default, such a listener is only called when a Table is added or removed. To listen to all changes in the Store, use the addTablesListener method.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Table Ids.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addTableIdsListener((store) => {
  console.log('Table Ids changed');
  console.log(store.getTableIds());
});

store.setTable('species', {dog: {price: 5}});
// -> 'Table Ids changed'
// -> ['pets', 'species']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Table Ids, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addTableIdsListener(
  (store) => store.setCell('meta', 'update', 'store', true),
  true, // mutator
);

store.setTable('species', {dog: {price: 5}});
console.log(store.getTable('meta'));
// -> {update: {store: true}}

store.delListener(listenerId);
Since

v1.0.0

addHasTableCellListener

The addHasTableCellListener method registers a listener function with the Store that will be called when a Cell is added to or removed from anywhere in a Table as a whole.

addHasTableCellListener(
  tableId: IdOrNull,
  cellId: IdOrNull,
  listener: HasTableCellListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerHasTableCellListener<MergeableStore>

The function that will be called whenever the matching Cell is added to or removed from anywhere in the Table.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasTableCellListener function, and will be called with a reference to the Store, the Id of the Table that changed, and the Id of the Table Cell that changed. It is also given a flag to indicate whether the Cell now exists anywhere in the Table (having not done previously), or does not (having done so previously).

You can either listen to a single Table Cell being added or removed (by specifying the Table Id and Cell Id, as the method's first two parameters) or changes to any Table Cell (by providing null wildcards).

Both, either, or neither of the tableId and cellId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Cell being added to or removed from the Table as a whole.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableCellListener(
  'pets',
  'color',
  (store, tableId, cellId, hasTableCell) => {
    console.log(
      'color cell in pets table ' + (hasTableCell ? 'added' : 'removed'),
    );
  },
);

store.delCell('pets', 'fido', 'color');
// -> 'color cell in pets table removed'

store.setRow('pets', 'felix', {species: 'cat', color: 'brown'});
// -> 'color cell in pets table added'

store.delListener(listenerId);

This example registers a listener that responds to any Cell being added to or removed from the Table as a whole.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableCellListener(
  null,
  null,
  (store, tableId, cellId, hasTableCell) => {
    console.log(
      `${cellId} cell in ${tableId} table ` +
        (hasTableCell ? 'added' : 'removed'),
    );
  },
);

store.delCell('pets', 'fido', 'color');
// -> 'color cell in pets table removed'
store.setTable('species', {dog: {price: 5}});
// -> 'price cell in species table added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Cell being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableCellListener(
  'pets',
  'color',
  (store, tableId, cellId) =>
    store.setCell('meta', 'update', `${tableId}_${cellId}`, true),
  true,
);

store.delRow('pets', 'fido');
console.log(store.getTable('meta'));
// -> {update: {pets_color: true}}

store.delListener(listenerId);
Since

v4.4.0

addHasTableListener

The addHasTableListener method registers a listener function with the Store that will be called when a Table is added to or removed from the Store.

addHasTableListener(
  tableId: IdOrNull,
  listener: HasTableListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerHasTableListener<MergeableStore>

The function that will be called whenever the matching Table is added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasTableListener function, and will be called with a reference to the Store and the Id of the Table that changed. It is also given a flag to indicate whether the Table now exists (having not done previously), or does not (having done so previously).

You can either listen to a single Table being added or removed (by specifying the Table Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Table being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableListener(
  'pets',
  (store, tableId, hasTable) => {
    console.log('pets table ' + (hasTable ? 'added' : 'removed'));
  },
);

store.delTable('pets');
// -> 'pets table removed'

store.setTable('pets', {fido: {species: 'dog', color: 'brown'}});
// -> 'pets table added'

store.delListener(listenerId);

This example registers a listener that responds to any Table being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableListener(
  null,
  (store, tableId, hasTable) => {
    console.log(`${tableId} table ` + (hasTable ? 'added' : 'removed'));
  },
);

store.delTable('pets');
// -> 'pets table removed'
store.setTable('species', {dog: {price: 5}});
// -> 'species table added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Table being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasTableListener(
  'pets',
  (store, tableId) => store.setCell('meta', 'update', tableId, true),
  true,
);

store.delTable('pets', 'fido');
console.log(store.getTable('meta'));
// -> {update: {pets: true}}

store.delListener(listenerId);
Since

v4.4.0

addTableCellIdsListener

The addTableCellIdsListener method registers a listener function with the Store that will be called whenever the Cell Ids that appear anywhere in a Table change.

addTableCellIdsListener(
  tableId: IdOrNull,
  listener: TableCellIdsListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerTableCellIdsListener<MergeableStore>

The function that will be called whenever the Cell Ids that appear anywhere in a Table change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a TableCellIdsListener function, and will be called with a reference to the Store and the Id of the Table that changed.

By default, such a listener is only called when a Cell Id is added or removed from the whole of the Table. To listen to all changes in the Table, use the addTableListener method.

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Cell Ids that appear anywhere in a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableCellIdsListener('pets', (store) => {
  console.log('Cell Ids in pets table changed');
  console.log(store.getTableCellIds('pets'));
});

store.setRow('pets', 'felix', {species: 'cat', legs: 4});
// -> 'Cell Ids in pets table changed'
// -> ['species', 'color', 'legs']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Cell Ids that appear anywhere in any Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
  species: {dog: {price: 5}},
});
const listenerId = store.addTableCellIdsListener(
  null,
  (store, tableId) => {
    console.log(`Cell Ids in ${tableId} table changed`);
    console.log(store.getTableCellIds(tableId));
  },
);

store.setRow('pets', 'felix', {species: 'cat', legs: 4});
// -> 'Cell Ids in pets table changed'
// -> ['species', 'color', 'legs']

store.setRow('species', 'cat', {price: 4, friendly: true});
// -> 'Cell Ids in species table changed'
// -> ['price', 'friendly']

store.delListener(listenerId);

This example registers a listener that responds to the Cell Ids that appear anywhere in a Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableCellIdsListener(
  'pets',
  (store, tableId) => store.setCell('meta', 'update', tableId, true),
  true, // mutator
);

store.setRow('pets', 'felix', {species: 'cat', legs: 4});
console.log(store.getTable('meta'));
// -> {update: {pets: true}}

store.delListener(listenerId);
Since

v1.0.0

addTableListener

The addTableListener method registers a listener function with the Store that will be called whenever data in a Table changes.

addTableListener(
  tableId: IdOrNull,
  listener: TableListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerTableListener<MergeableStore>

The function that will be called whenever data in the matching Table changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a TableListener function, and will be called with a reference to the Store, the Id of the Table that changed, and a GetCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableListener(
  'pets',
  (store, tableId, getCellChange) => {
    console.log('pets table changed');
    console.log(getCellChange('pets', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'pets table changed'
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableListener(null, (store, tableId) => {
  console.log(`${tableId} table changed`);
});

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'pets table changed'
store.setTable('species', {dog: {price: 5}});
// -> 'species table changed'

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTableListener(
  'pets',
  (store, tableId) => store.setCell('meta', 'update', tableId, true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {pets: true}}

store.delListener(listenerId);
Since

v1.0.0

addRowIdsListener

The addRowIdsListener method registers a listener function with the Store that will be called whenever the Row Ids in a Table change.

addRowIdsListener(
  tableId: IdOrNull,
  listener: RowIdsListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerRowIdsListener<MergeableStore>

The function that will be called whenever the Row Ids in the Table change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a RowIdsListener function, and will be called with a reference to the Store and the Id of the Table that changed.

By default, such a listener is only called when a Row is added or removed. To listen to all changes in the Table, use the addTableListener method.

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Row Ids of a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowIdsListener('pets', (store) => {
  console.log('Row Ids for pets table changed');
  console.log(store.getRowIds('pets'));
});

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Row Ids for pets table changed'
// -> ['fido', 'felix']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Row Ids of any Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowIdsListener(null, (store, tableId) => {
  console.log(`Row Ids for ${tableId} table changed`);
  console.log(store.getRowIds(tableId));
});

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Row Ids for pets table changed'
// -> ['fido', 'felix']
store.setRow('species', 'dog', {price: 5});
// -> 'Row Ids for species table changed'
// -> ['dog']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Row Ids of a specific Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowIdsListener(
  'pets',
  (store, tableId) => store.setCell('meta', 'update', tableId, true),
  true, // mutator
);

store.setRow('pets', 'felix', {species: 'cat'});
console.log(store.getTable('meta'));
// -> {update: {pets: true}}

store.delListener(listenerId);
Since

v1.0.0

addSortedRowIdsListener

The addSortedRowIdsListener method registers a listener function with the Store that will be called whenever sorted (and optionally, paginated) Row Ids in a Table change.

addSortedRowIdsListener(
  tableId: string,
  cellId: undefined | string,
  descending: boolean,
  offset: number,
  limit: undefined | number,
  listener: SortedRowIdsListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdstring

The Id of the Table to listen to.

cellIdundefined | string

The Id of the Cell whose values are used for the sorting, or undefined to sort by the Row Id itself.

descendingboolean

Whether the sorting should be in descending order.

offsetnumber

The number of Row Ids to skip for pagination purposes, if any.

limitundefined | number

The maximum number of Row Ids to return, or undefined for all.

listenerSortedRowIdsListener<MergeableStore>

The function that will be called whenever the sorted Row Ids in the Table change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a SortedRowIdsListener function, and will be called with a reference to the Store, the Id of the Table whose Row Ids sorting changed, the Cell Id being used to sort them, whether descending or not, and the offset and limit of the number of Ids returned, for pagination purposes. It also receives the sorted array of Ids itself, so that you can use them in the listener without the additional cost of an explicit call to getSortedRowIds.

Such a listener is called when a Row is added or removed, but also when a value in the specified Cell (somewhere in the Table) has changed enough to change the sorting of the Row Ids.

Unlike most other listeners, you cannot provide wildcards (due to the cost of detecting changes to the sorting). You can only listen to a single specified Table, sorted by a single specified Cell.

The sorting of the rows is alphanumeric, and you can indicate whether it should be in descending order. The offset and limit parameters are used to paginate results, but default to 0 and undefined to return all available Row Ids if not specified.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the sorted Row Ids of a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    cujo: {species: 'wolf'},
    felix: {species: 'cat'},
  },
});
console.log(store.getSortedRowIds('pets', 'species', false));
// -> ['felix', 'cujo']

const listenerId = store.addSortedRowIdsListener(
  'pets',
  'species',
  false,
  0,
  undefined,
  (store, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`Sorted Row Ids for ${tableId} table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);

store.setRow('pets', 'fido', {species: 'dog'});
// -> 'Sorted Row Ids for pets table changed'
// -> ['felix', 'fido', 'cujo']

store.delListener(listenerId);

This example registers a listener that responds to any change to a paginated section of the sorted Row Ids of a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {price: 6},
    felix: {price: 5},
    mickey: {price: 2},
    tom: {price: 4},
    carnaby: {price: 3},
    lowly: {price: 1},
  },
});

const listenerId = store.addSortedRowIdsListener(
  'pets',
  'price',
  false,
  0,
  3,
  (store, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`First three sorted Row Ids for ${tableId} table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);
console.log(store.getSortedRowIds('pets', 'price', false, 0, 3));
// -> ['lowly', 'mickey', 'carnaby']

store.setCell('pets', 'carnaby', 'price', 4.5);
// -> 'First three sorted Row Ids for pets table changed'
// -> ['lowly', 'mickey', 'tom']

store.delListener(listenerId);

This example registers a listener that responds to any change to the sorted Row Ids of a specific Table. The Row Ids are sorted by their own value, since the cellId parameter is explicitly undefined.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getSortedRowIds('pets', undefined, false));
// -> ['felix', 'fido']

const listenerId = store.addSortedRowIdsListener(
  'pets',
  undefined,
  false,
  0,
  undefined,
  (store, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`Sorted Row Ids for ${tableId} table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);

store.setRow('pets', 'cujo', {species: 'wolf'});
// -> 'Sorted Row Ids for pets table changed'
// -> ['cujo', 'felix', 'fido']

store.delListener(listenerId);

This example registers a listener that responds to a change in the sorting of the rows of a specific Table, even though the set of Ids themselves has not changed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
console.log(store.getSortedRowIds('pets', 'species', false));
// -> ['felix', 'fido']

const listenerId = store.addSortedRowIdsListener(
  'pets',
  'species',
  false,
  0,
  undefined,
  (store, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`Sorted Row Ids for ${tableId} table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);

store.setCell('pets', 'felix', 'species', 'tiger');
// -> 'Sorted Row Ids for pets table changed'
// -> ['fido', 'felix']

store.delListener(listenerId);

This example registers a listener that responds to any change to the sorted Row Ids of a specific Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    cujo: {species: 'wolf'},
    felix: {species: 'cat'},
  },
});
const listenerId = store.addSortedRowIdsListener(
  'pets',
  'species',
  false,
  0,
  undefined,
  (store, tableId) => store.setCell('meta', 'sorted', tableId, true),
  true, // mutator
);

store.setRow('pets', 'fido', {species: 'dog'});
console.log(store.getTable('meta'));
// -> {sorted: {pets: true}}

store.delListener(listenerId);
Since

v2.0.0

addHasRowListener

The addHasRowListener method registers a listener function with the Store that will be called when a Row is added to or removed from the Store.

addHasRowListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: HasRowListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerHasRowListener<MergeableStore>

The function that will be called whenever the matching Row is added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasRowListener function, and will be called with a reference to the Store, the Id of the Table that changed, and the Id of the Row that changed. It is also given a flag to indicate whether the Row now exists (having not done previously), or does not (having done so previously).

You can either listen to a single Row being added or removed (by specifying the Table Id and Row Id, as the method's first two parameters) or changes to any Row (by providing null wildcards).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Row being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasRowListener(
  'pets',
  'fido',
  (store, tableId, rowId, hasRow) => {
    console.log(
      'fido row in pets table ' + (hasRow ? 'added' : 'removed'),
    );
  },
);

store.delRow('pets', 'fido');
// -> 'fido row in pets table removed'

store.setRow('pets', 'fido', {species: 'dog', color: 'brown'});
// -> 'fido row in pets table added'

store.delListener(listenerId);

This example registers a listener that responds to any Row being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasRowListener(
  null,
  null,
  (store, tableId, rowId, hasRow) => {
    console.log(
      `${rowId} row in ${tableId} table ` + (hasRow ? 'added' : 'removed'),
    );
  },
);

store.delRow('pets', 'fido');
// -> 'fido row in pets table removed'
store.setTable('species', {dog: {price: 5}});
// -> 'dog row in species table added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Row being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasRowListener(
  'pets',
  'fido',
  (store, tableId, rowId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}`, true),
  true,
);

store.delRow('pets', 'fido');
console.log(store.getTable('meta'));
// -> {update: {pets_fido: true}}

store.delListener(listenerId);
Since

v4.4.0

addRowCountListener

The addRowCountListener method registers a listener function with the Store that will be called whenever the count of Row objects in a Table change.

addRowCountListener(
  tableId: IdOrNull,
  listener: RowCountListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerRowCountListener<MergeableStore>

The function that will be called whenever the number of Row objects in the Table changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a RowCountListener function, and will be called with a reference to the Store, the Id of the Table that changed, and the number of Row objects in the Table.

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a change in the number of Row objects in a specific Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowCountListener(
  'pets',
  (store, _tableId, count) => {
    console.log('Row count for pets table changed to ' + count);
  },
);

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Row count for pets table changed to 2'

store.delListener(listenerId);

This example registers a listener that responds to any change to a change in the number of Row objects of any Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowCountListener(
  null,
  (store, tableId, count) => {
    console.log(`Row count for ${tableId} table changed to ${count}`);
  },
);

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Row count for pets table changed to 2'
store.setRow('species', 'dog', {price: 5});
// -> 'Row count for species table changed to 1'

store.delListener(listenerId);

This example registers a listener that responds to any change to a change in the number of Row objects of a specific Table, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addRowCountListener(
  'pets',
  (store, tableId, count) =>
    store.setCell('meta', 'update', tableId, count),
  true, // mutator
);

store.setRow('pets', 'felix', {species: 'cat'});
console.log(store.getTable('meta'));
// -> {update: {pets: 2}}

store.delListener(listenerId);
Since

v4.1.0

addRowListener

The addRowListener method registers a listener function with the Store that will be called whenever data in a Row changes.

addRowListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: RowListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerRowListener<MergeableStore>

The function that will be called whenever data in the matching Row changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a RowListener function, and will be called with a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, and a GetCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single Row (by specifying the Table Id and Row Id as the method's first two parameters) or changes to any Row (by providing null wildcards).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to a specific Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addRowListener(
  'pets',
  'fido',
  (store, tableId, rowId, getCellChange) => {
    console.log('fido row in pets table changed');
    console.log(getCellChange('pets', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'fido row in pets table changed'
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addRowListener(
  null,
  null,
  (store, tableId, rowId) => {
    console.log(`${rowId} row in ${tableId} table changed`);
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'fido row in pets table changed'
store.setTable('species', {dog: {price: 5}});
// -> 'dog row in species table changed'

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Row, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addRowListener(
  'pets',
  'fido',
  (store, tableId, rowId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}`, true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {pets_fido: true}}

store.delListener(listenerId);
Since

v1.0.0

addCellIdsListener

The addCellIdsListener method registers a listener function with the Store that will be called whenever the Cell Ids in a Row change.

addCellIdsListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: CellIdsListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerCellIdsListener<MergeableStore>

The function that will be called whenever the Cell Ids in the Row change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a CellIdsListener function, and will be called with a reference to the Store, the Id of the Table, and the Id of the Row that changed.

By default, such a listener is only called when a Cell is added or removed. To listen to all changes in the Row, use the addRowListener method.

You can either listen to a single Row (by specifying the Table Id and Row Id as the method's first two parameters) or changes to any Row (by providing a null wildcard).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Cell Ids of a specific Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addCellIdsListener('pets', 'fido', (store) => {
  console.log('Cell Ids for fido row in pets table changed');
  console.log(store.getCellIds('pets', 'fido'));
});

store.setCell('pets', 'fido', 'color', 'brown');
// -> 'Cell Ids for fido row in pets table changed'
// -> ['species', 'color']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Cell Ids of any Row.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addCellIdsListener(
  null,
  null,
  (store, tableId, rowId) => {
    console.log(`Cell Ids for ${rowId} row in ${tableId} table changed`);
    console.log(store.getCellIds(tableId, rowId));
  },
);

store.setCell('pets', 'fido', 'color', 'brown');
// -> 'Cell Ids for fido row in pets table changed'
// -> ['species', 'color']
store.setCell('species', 'dog', 'price', 5);
// -> 'Cell Ids for dog row in species table changed'
// -> ['price']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Cell Ids of a specific Row, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const listenerId = store.addCellIdsListener(
  'pets',
  'fido',
  (store, tableId, rowId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}`, true),
  true, // mutator
);

store.setCell('pets', 'fido', 'color', 'brown');
console.log(store.getTable('meta'));
// -> {update: {pets_fido: true}}

store.delListener(listenerId);
Since

v1.0.0

addCellListener

The addCellListener method registers a listener function with the Store that will be called whenever data in a Cell changes.

addCellListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: CellListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerCellListener<MergeableStore>

The function that will be called whenever data in the matching Cell changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a CellListener function, and will be called with a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, the Id of the Cell that changed, the new Cell value, the old Cell value, and a GetCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single Cell (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or changes to any Cell (by providing null wildcards).

All, some, or none of the tableId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific Row in a specific Table, any Cell in any Row in any Table, for example - or every other combination of wildcards.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to a specific Cell.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, newCell, oldCell, getCellChange) => {
    console.log('color cell in fido row in pets table changed');
    console.log([oldCell, newCell]);
    console.log(getCellChange('pets', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'color cell in fido row in pets table changed'
// -> ['brown', 'walnut']
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any Cell.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId) => {
    console.log(
      `${cellId} cell in ${rowId} row in ${tableId} table changed`,
    );
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'color cell in fido row in pets table changed'
store.setTable('species', {dog: {price: 5}});
// -> 'price cell in dog row in species table changed'

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Cell, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}_${cellId}`, true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {pets_fido_color: true}}

store.delListener(listenerId);
Since

v1.0.0

addHasCellListener

The addHasCellListener method registers a listener function with the Store that will be called when a Cell is added to or removed from the Store.

addHasCellListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: HasCellListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerHasCellListener<MergeableStore>

The function that will be called whenever the matching Cell is added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasCellListener function, and will be called with a reference to the Store, the Id of the Table that changed, the Id of the Row that changed, and the Id of the Cell that changed. It is also given a flag to indicate whether the Cell now exists (having not done previously), or does not (having done so previously).

You can either listen to a single Cell being added or removed (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or changes to any Cell (by providing null wildcards).

All, some, or none of the tableId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific Row in a specific Table, any Cell in any Row in any Table, for example - or every other combination of wildcards.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Cell being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, hasCell) => {
    console.log(
      'color cell in fido row in pets table ' +
        (hasCell ? 'added' : 'removed'),
    );
  },
);

store.delCell('pets', 'fido', 'color');
// -> 'color cell in fido row in pets table removed'

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'color cell in fido row in pets table added'

store.delListener(listenerId);

This example registers a listener that responds to any Cell being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId, hasCell) => {
    console.log(
      `${cellId} cell in ${rowId} row in ${tableId} table ` +
        (hasCell ? 'added' : 'removed'),
    );
  },
);

store.delCell('pets', 'fido', 'color');
// -> 'color cell in fido row in pets table removed'
store.setTable('species', {dog: {price: 5}});
// -> 'price cell in dog row in species table added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Cell being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addHasCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId) =>
    store.setCell('meta', 'update', `${tableId}_${rowId}_${cellId}`, true),
  true,
);

store.delCell('pets', 'fido', 'color');
console.log(store.getTable('meta'));
// -> {update: {pets_fido_color: true}}

store.delListener(listenerId);
Since

v4.4.0

addInvalidCellListener

The addInvalidCellListener method registers a listener function with the Store that will be called whenever invalid data was attempted to be written to a Cell.

addInvalidCellListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: InvalidCellListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerInvalidCellListener<MergeableStore>

The function that will be called whenever an attempt to write invalid data to the matching Cell was made.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is an InvalidCellListener function, and will be called with a reference to the Store, the Id of the Table, the Id of the Row, and the Id of Cell that was being attempted to be changed. It is also given the invalid value of the Cell, which could have been of absolutely any type. Since there could have been multiple failed attempts to set the Cell within a single transaction, this is an array containing each attempt, chronologically.

You can either listen to a single Cell (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or invalid attempts to change any Cell (by providing null wildcards).

All, some, or none of the tableId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific Row in a specific Table, any Cell in any Row in any Table, for example - or every other combination of wildcards.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Special note should be made for how the listener will be called when a TablesSchema is present. The listener will be called:

The listener will not be called if a Cell that is defaulted in the TablesSchema is not provided, as long as all of the Cells that are not defaulted are provided.

To help understand all of these schema-based conditions, please see the TablesSchema example below.

Examples

This example registers a listener that responds to any invalid changes to a specific Cell.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addInvalidCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, invalidCells) => {
    console.log('Invalid color cell in fido row in pets table');
    console.log(invalidCells);
  },
);

store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
// -> 'Invalid color cell in fido row in pets table'
// -> [{r: '96', g: '4B', b: '00'}]

store.delListener(listenerId);

This example registers a listener that responds to any invalid changes to any Cell - in a Store without a TablesSchema. Note also how it then responds to cases where empty or invalid Row objects, or Table objects, or Tables objects are provided.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addInvalidCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId) => {
    console.log(
      `Invalid ${cellId} cell in ${rowId} row in ${tableId} table`,
    );
  },
);

store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
// -> 'Invalid color cell in fido row in pets table'
store.setTable('sales', {fido: {date: new Date()}});
// -> 'Invalid date cell in fido row in sales table'

store.setRow('pets', 'felix', {});
// -> 'Invalid undefined cell in felix row in pets table'

store.setRow('filter', 'name', /[a-z]?/);
// -> 'Invalid undefined cell in name row in filter table'

store.setRow('sales', '2021', {forecast: undefined});
// -> 'Invalid forecast cell in 2021 row in sales table'

store.addRow('filter', /[0-9]?/);
// -> 'Invalid undefined cell in undefined row in filter table'

store.setTable('raw', {});
// -> 'Invalid undefined cell in undefined row in raw table'

store.setTable('raw', ['row1', 'row2']);
// -> 'Invalid undefined cell in undefined row in raw table'

store.setTables(['table1', 'table2']);
// -> 'Invalid undefined cell in undefined row in undefined table'

store.delListener(listenerId);

This example registers a listener that responds to any invalid changes to any Cell - in a Store with a TablesSchema. Note how it responds to cases where missing parameters are provided for optional, and defaulted Cell values in a Row.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {
    species: {type: 'string'},
    color: {type: 'string', default: 'unknown'},
  },
});

const listenerId = store.addInvalidCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId) => {
    console.log(
      `Invalid ${cellId} cell in ${rowId} row in ${tableId} table`,
    );
  },
);

store.setRow('sales', 'fido', {price: 5});
// -> 'Invalid price cell in fido row in sales table'
// The listener is called, because the sales Table is not in the schema

store.setRow('pets', 'felix', {species: true});
// -> 'Invalid species cell in felix row in pets table'
// The listener is called, because species is invalid...
console.log(store.getRow('pets', 'felix'));
// -> {color: 'unknown'}
// ...even though a Row was set with the default value

store.setRow('pets', 'fido', {color: 'brown'});
// -> 'Invalid species cell in fido row in pets table'
// The listener is called, because species is missing and not defaulted...
console.log(store.getRow('pets', 'fido'));
// -> {color: 'brown'}
// ...even though a Row was set

store.setRow('pets', 'rex', {species: 'dog'});
console.log(store.getRow('pets', 'rex'));
// -> {species: 'dog', color: 'unknown'}
// The listener is not called, because color is defaulted

store.delTables().setTablesSchema({
  pets: {
    species: {type: 'string'},
    color: {type: 'string'},
  },
});

store.setRow('pets', 'cujo', {});
// -> 'Invalid species cell in cujo row in pets table'
// -> 'Invalid color cell in cujo row in pets table'
// -> 'Invalid undefined cell in cujo row in pets table'
// The listener is called multiple times, because neither Cell is defaulted
// and the Row as a whole is empty

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Cell, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addInvalidCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, invalidCells) =>
    store.setCell(
      'meta',
      'invalid_updates',
      `${tableId}_${rowId}_${cellId}`,
      JSON.stringify(invalidCells[0]),
    ),
  true,
);

store.setCell('pets', 'fido', 'color', {r: '96', g: '4B', b: '00'});
console.log(store.getRow('meta', 'invalid_updates'));
// -> {'pets_fido_color': '{"r":"96","g":"4B","b":"00"}'}

store.delListener(listenerId);
Since

v1.1.0

addHasValuesListener

The addHasValuesListener method registers a listener function with the Store that will be called when Values as a whole are added to or removed from the Store.

addHasValuesListener(
  listener: HasValuesListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
listenerHasValuesListener<MergeableStore>

The function that will be called whenever Values as a whole are added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasValuesListener function, and will be called with a reference to the Store. It is also given a flag to indicate whether Values now exist (having not done previously), or do not (having done so previously).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to Values being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addHasValuesListener((store, hasValues) => {
  console.log('Values ' + (hasValues ? 'added' : 'removed'));
});

store.delValues();
// -> 'Values removed'

store.setValue('employees', 4);
// -> 'Values added'

store.delListener(listenerId);

This example registers a listener that responds to Values being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore();
const listenerId = store.addHasValuesListener(
  (store, hasValues) => store.setValue('hasValues', hasValues),
  true,
);

store.setValue('employees', 4);
console.log(store.getValues());
// -> {employees: 4, hasValues: true}

store.delListener(listenerId);
Since

v4.4.0

addValuesListener

The addValuesListener method registers a listener function with the Store that will be called whenever the Values change.

addValuesListener(
  listener: ValuesListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
listenerValuesListener<MergeableStore>

The function that will be called whenever data in the Values changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a ValuesListener function, and will be called with a reference to the Store and a GetValueChange function in case you need to inspect any changes that occurred.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to the Store's Values.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValuesListener((store, getValueChange) => {
  console.log('values changed');
  console.log(getValueChange('employees'));
});

store.setValue('employees', 4);
// -> 'values changed'
// -> [true, 3, 4]

store.delListener(listenerId);

This example registers a listener that responds to any changes to the Store's Values, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValuesListener(
  (store) => store.setValue('updated', true),
  true,
);

store.setValue('employees', 4);
console.log(store.getValues());
// -> {open: true, employees: 4, updated: true}

store.delListener(listenerId);
Since

v3.0.0

addHasValueListener

The addHasValueListener method registers a listener function with the Store that will be called when a Value is added to or removed from the Store.

addHasValueListener(
  valueId: IdOrNull,
  listener: HasValueListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
valueIdIdOrNull

The Id of the Value to listen to, or null as a wildcard.

listenerHasValueListener<MergeableStore>

The function that will be called whenever the matching Value is added or removed.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a HasValueListener function, and will be called with a reference to the Store and the Id of Value that changed. It is also given a flag to indicate whether the Value now exists (having not done previously), or does not (having done so previously).

You can either listen to a single Value being added or removed (by specifying the Value Id) or any Value being added or removed (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to a specific Value being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addHasValueListener(
  'employees',
  (store, valueId, hasValue) => {
    console.log('employee value ' + (hasValue ? 'added' : 'removed'));
  },
);

store.delValue('employees');
// -> 'employee value removed'

store.setValue('employees', 4);
// -> 'employee value added'

store.delListener(listenerId);

This example registers a listener that responds to any Value being added or removed.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addHasValueListener(
  null,
  (store, valueId, hasValue) => {
    console.log(valueId + ' value ' + (hasValue ? 'added' : 'removed'));
  },
);

store.delValue('employees');
// -> 'employees value removed'
store.setValue('website', 'https://pets.com');
// -> 'website value added'

store.delListener(listenerId);

This example registers a listener that responds to a specific Value being added or removed, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addHasValueListener(
  'employees',
  (store) => store.setValue('updated', true),
  true,
);

store.delValue('employees');
console.log(store.getValues());
// -> {open: true, updated: true}

store.delListener(listenerId);
Since

v4.4.0

addInvalidValueListener

The addInvalidValueListener method registers a listener function with the Store that will be called whenever invalid data was attempted to be written to a Value.

addInvalidValueListener(
  valueId: IdOrNull,
  listener: InvalidValueListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
valueIdIdOrNull

The Id of the Value to listen to, or null as a wildcard.

listenerInvalidValueListener<MergeableStore>

The function that will be called whenever an attempt to write invalid data to the matching Value was made.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is an InvalidValueListener function, and will be called with a reference to the Store and the Id of Value that was being attempted to be changed. It is also given the invalid value of the Value, which could have been of absolutely any type. Since there could have been multiple failed attempts to set the Value within a single transaction, this is an array containing each attempt, chronologically.

You can either listen to a single Value (by specifying the Value Id as the method's first parameter) or invalid attempts to change any Value (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Special note should be made for how the listener will be called when a ValuesSchema is present. The listener will be called:

The listener will not be called if a Value that is defaulted in the ValuesSchema is not provided, as long as all of the Values that are not defaulted are provided.

To help understand all of these schema-based conditions, please see the ValuesSchema example below.

Examples

This example registers a listener that responds to any invalid changes to a specific Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addInvalidValueListener(
  'open',
  (store, valueId, invalidValues) => {
    console.log('Invalid open value');
    console.log(invalidValues);
  },
);

store.setValue('open', {yes: true});
// -> 'Invalid open value'
// -> [{yes: true}]

store.delListener(listenerId);

This example registers a listener that responds to any invalid changes to any Value - in a Store without a ValuesSchema. Note also how it then responds to cases where an empty Values object is provided.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addInvalidValueListener(
  null,
  (store, valueId) => {
    console.log(`Invalid ${valueId} value`);
  },
);

store.setValue('open', {yes: true});
// -> 'Invalid open value'
store.setValue('employees', ['alice', 'bob']);
// -> 'Invalid employees value'

store.setValues('pets', 'felix', {});
// -> 'Invalid undefined value'

store.delListener(listenerId);

This example registers a listener that responds to any invalid changes to any Value - in a Store with a ValuesSchema. Note how it responds to cases where missing parameters are provided for optional, and defaulted Values.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({
  open: {type: 'boolean', default: false},
  employees: {type: 'number'},
});

console.log(store.getValues());
// -> {open: false}

const listenerId = store.addInvalidValueListener(
  null,
  (store, valueId) => {
    console.log(`Invalid ${valueId} value`);
  },
);

store.setValue('website', true);
// -> 'Invalid website value'
// The listener is called, because the website Value is not in the schema

store.setValue('open', 'yes');
// -> 'Invalid open value'
// The listener is called, because 'open' is invalid...
console.log(store.getValues());
// -> {open: false}
// ...even though it is still present with the default value

store.setValues({open: true});
// -> 'Invalid employees value'
// The listener is called because employees is missing and not defaulted...
console.log(store.getValues());
// -> {open: true}
// ...even though the Values were set

store.setValues({employees: 3});
console.log(store.getValues());
// -> {open: false, employees: 3}
// The listener is not called, because 'open' is defaulted

store.setValuesSchema({
  open: {type: 'boolean'},
  employees: {type: 'number'},
});

store.setValues({});
// -> 'Invalid open value'
// -> 'Invalid employees value'
// -> 'Invalid undefined value'
// The listener is called multiple times, because neither Value is
// defaulted and the Values as a whole were empty

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Value, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addInvalidValueListener(
  'open',
  (store, valueId, invalidValues) =>
    store.setValue('invalid_updates', JSON.stringify(invalidValues[0])),
  true,
);

store.setValue('open', {yes: true});
console.log(store.getValue('invalid_updates'));
// -> '{"yes":true}'

store.delListener(listenerId);
Since

v3.0.0

addValueIdsListener

The addValueIdsListener method registers a listener function with the Store that will be called whenever the Value Ids in a Store change.

addValueIdsListener(
  listener: ValueIdsListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
listenerValueIdsListener<MergeableStore>

The function that will be called whenever the Value Ids in the Store change.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a ValueIdsListener function, and will be called with a reference to the Store.

By default, such a listener is only called when a Value is added or removed. To listen to all changes in the Values, use the addValuesListener method.

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any change to the Value Ids.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addValueIdsListener((store) => {
  console.log('Value Ids changed');
  console.log(store.getValueIds());
});

store.setValue('employees', 3);
// -> 'Value Ids changed'
// -> ['open', 'employees']

store.delListener(listenerId);

This example registers a listener that responds to any change to the Value Ids, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const listenerId = store.addValueIdsListener(
  (store) => store.setValue('updated', true),
  true, // mutator
);

store.setValue('employees', 3);
console.log(store.getValues());
// -> {open: true, employees: 3, updated: true}

store.delListener(listenerId);
Since

v3.0.0

addValueListener

The addValueListener method registers a listener function with the Store that will be called whenever data in a Value changes.

addValueListener(
  valueId: IdOrNull,
  listener: ValueListener<MergeableStore>,
  mutator?: boolean,
): string
TypeDescription
valueIdIdOrNull

The Id of the Value to listen to, or null as a wildcard.

listenerValueListener<MergeableStore>

The function that will be called whenever data in the matching Value changes.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

returnsstring

A unique Id for the listener that can later be used to call it explicitly, or to remove it.

The provided listener is a ValueListener function, and will be called with a reference to the Store, the Id of the Value that changed, the new Value value, the old Value, and a GetValueChange function in case you need to inspect any changes that occurred.

You can either listen to a single Value (by specifying the Value Id) or changes to any Value (by providing a null wildcard).

Use the optional mutator parameter to indicate that there is code in the listener that will mutate Store data. If set to false (or omitted), such mutations will be silently ignored. All relevant mutator listeners (with this flag set to true) are called before any non-mutator listeners (since the latter may become relevant due to changes made in the former). The changes made by mutator listeners do not fire other mutating listeners, though they will fire non-mutator listeners.

Examples

This example registers a listener that responds to any changes to a specific Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValueListener(
  'employees',
  (store, valueId, newValue, oldValue, getValueChange) => {
    console.log('employee value changed');
    console.log([oldValue, newValue]);
    console.log(getValueChange('employees'));
  },
);

store.setValue('employees', 4);
// -> 'employee value changed'
// -> [3, 4]
// -> [true, 3, 4]

store.delListener(listenerId);

This example registers a listener that responds to any changes to any Value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValueListener(null, (store, valueId) => {
  console.log(`${valueId} value changed`);
});

store.setValue('employees', 4);
// -> 'employees value changed'
store.setValue('open', false);
// -> 'open value changed'

store.delListener(listenerId);

This example registers a listener that responds to any changes to a specific Value, and which also mutates the Store itself.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const listenerId = store.addValueListener(
  'employees',
  (store) => store.setValue('updated', true),
  true,
);

store.delValue('employees');
console.log(store.getValues());
// -> {open: true, updated: true}

store.delListener(listenerId);
Since

v3.0.0

addDidFinishTransactionListener

The addDidFinishTransactionListener method registers a listener function with the Store that will be called just after other non-mutating listeners are called at the end of the transaction.

addDidFinishTransactionListener(listener: TransactionListener<MergeableStore>): string
TypeDescription
listenerTransactionListener<MergeableStore>

The function that will be called after the end of a transaction.

returnsstring

A unique Id for the listener that can later be used to remove it.

This is useful if you need to know that a set of listeners have just been called at the end of a transaction, perhaps to batch their consequences together.

The provided TransactionListener will receive a reference to the Store and two booleans to indicate whether Cell or Value data has been touched during the transaction. The two flags is intended as a hint about whether non-mutating listeners might have been called at the end of the transaction.

Here, 'touched' means that Cell or Value data has either been changed, or changed and then changed back to its original value during the transaction. The exception is a transaction that has been rolled back, for which the value of cellsTouched and valuesTouched in the listener will be false because all changes have been reverted.

Note that a TransactionListener added to the Store with this method cannot mutate the Store itself, and attempts to do so will fail silently.

Example

This example registers a listener that is called at the end of the transaction, just after its listeners have been called. The transactions shown here variously change, touch, and rollback cells, demonstrating how the cellsTouched and valuesTouched parameters in the listener work.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', color: 'brown'}},
  })
  .setValues({open: true, employees: 3});
const listenerId = store.addDidFinishTransactionListener((store) => {
  const [cellsTouched, valuesTouched] = store.getTransactionLog() ?? {};
  console.log(`Cells/Values touched: ${cellsTouched}/${valuesTouched}`);
});
const listenerId2 = store.addTablesListener(() =>
  console.log('Tables changed'),
);
const listenerId3 = store.addValuesListener(() =>
  console.log('Values changed'),
);

store.transaction(() =>
  store.setCell('pets', 'fido', 'color', 'brown').setValue('employees', 3),
);
// -> 'Cells/Values touched: false/false'

store.transaction(() => store.setCell('pets', 'fido', 'color', 'walnut'));
// -> 'Tables changed'
// -> 'Cells/Values touched: true/false'

store.transaction(() => store.setValue('employees', 4));
// -> 'Values changed'
// -> 'Cells/Values touched: false/true'

store.transaction(() => {
  store
    .setRow('pets', 'felix', {species: 'cat'})
    .delRow('pets', 'felix')
    .setValue('city', 'London')
    .delValue('city');
});
// -> 'Cells/Values touched: true/true'
// But no Tables or Values listeners fired since there are no net changes.

store.transaction(
  () =>
    store
      .setRow('pets', 'felix', {species: 'cat'})
      .setValue('city', 'London'),
  () => true,
);
// -> 'Cells/Values touched: false/false'
// Transaction was rolled back.

store.callListener(listenerId);
// -> 'Cells/Values touched: false/false'
// It is meaningless to call this listener directly.

store
  .delListener(listenerId)
  .delListener(listenerId2)
  .delListener(listenerId3);
Since

v1.3.0

addStartTransactionListener

The addStartTransactionListener method registers a listener function with the Store that will be called at the start of a transaction.

addStartTransactionListener(listener: TransactionListener<MergeableStore>): string
TypeDescription
listenerTransactionListener<MergeableStore>

The function that will be called at the start of a transaction.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided TransactionListener will receive a reference to the Store and two booleans to indicate whether Cell or Value data has been touched during the transaction. Since this is called at the start, they will both be false!

Note that a TransactionListener added to the Store with this method can mutate the Store, and its changes will be treated as part of the transaction that is starting.

Example

This example registers a listener that is called at start end of the transaction, just before its listeners will be called.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', color: 'brown'}},
  })
  .setValues({open: true, employees: 3});
const listenerId = store.addStartTransactionListener(() => {
  console.log('Transaction started');
});

store.transaction(() =>
  store.setCell('pets', 'fido', 'color', 'brown').setValue('employees', 3),
);
// -> 'Transaction started'

store.callListener(listenerId);
// -> 'Transaction started'

store.delListener(listenerId);
Since

v3.2.0

addWillFinishTransactionListener

The addWillFinishTransactionListener method registers a listener function with the Store that will be called just before other non-mutating listeners are called at the end of the transaction.

addWillFinishTransactionListener(listener: TransactionListener<MergeableStore>): string
TypeDescription
listenerTransactionListener<MergeableStore>

The function that will be called before the end of a transaction.

returnsstring

A unique Id for the listener that can later be used to remove it.

This is useful if you need to know that a set of listeners are about to be called at the end of a transaction, perhaps to batch their consequences together.

The provided TransactionListener will receive a reference to the Store and two booleans to indicate whether Cell or Value data has been touched during the transaction. The two flags are intended as a hint about whether non-mutating listeners might be being called at the end of the transaction.

Here, 'touched' means that Cell or Value data has either been changed, or changed and then changed back to its original value during the transaction. The exception is a transaction that has been rolled back, for which the value of cellsTouched and valuesTouched in the listener will be false because all changes have been reverted.

Note that a TransactionListener added to the Store with this method can mutate the Store itself, and its changes will be treated as part of the transaction that is starting (and may fire non-mutating listeners after this).

Example

This example registers a listener that is called at the end of the transaction, just before its listeners will be called. The transactions shown here variously change, touch, and rollback cells, demonstrating how the cellsTouched and valuesTouched parameters in the listener work.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', color: 'brown'}},
  })
  .setValues({open: true, employees: 3});
const listenerId = store.addWillFinishTransactionListener((store) => {
  const [cellsTouched, valuesTouched] = store.getTransactionLog() ?? {};
  console.log(`Cells/Values touched: ${cellsTouched}/${valuesTouched}`);
});
const listenerId2 = store.addTablesListener(() =>
  console.log('Tables changed'),
);
const listenerId3 = store.addValuesListener(() =>
  console.log('Values changed'),
);

store.transaction(() =>
  store.setCell('pets', 'fido', 'color', 'brown').setValue('employees', 3),
);
// -> 'Cells/Values touched: false/false'

store.transaction(() => store.setCell('pets', 'fido', 'color', 'walnut'));
// -> 'Cells/Values touched: true/false'
// -> 'Tables changed'

store.transaction(() => store.setValue('employees', 4));
// -> 'Cells/Values touched: false/true'
// -> 'Values changed'

store.transaction(() => {
  store
    .setRow('pets', 'felix', {species: 'cat'})
    .delRow('pets', 'felix')
    .setValue('city', 'London')
    .delValue('city');
});
// -> 'Cells/Values touched: true/true'
// But no Tables or Values listeners fired since there are no net changes.

store.transaction(
  () =>
    store
      .setRow('pets', 'felix', {species: 'cat'})
      .setValue('city', 'London'),
  () => true,
);
// -> 'Cells/Values touched: false/false'
// Transaction was rolled back.

store.callListener(listenerId);
// -> 'Cells/Values touched: false/false'
// It is meaningless to call this listener directly.

store
  .delListener(listenerId)
  .delListener(listenerId2)
  .delListener(listenerId3);
Since

v1.3.0

callListener

The callListener method provides a way for you to manually provoke a listener to be called, even if the underlying data hasn't changed.

callListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to call.

returnsthis

A reference to the Store.

This is useful when you are using mutator listeners to guarantee that data conforms to programmatic conditions, and those conditions change such that you need to update the Store in bulk.

Examples

This example registers a listener that ensures a Cell has one of list of a valid values. After that list changes, the listener is called to apply the condition to the existing data.

import {createStore} from 'tinybase';

const validColors = ['walnut', 'brown', 'black'];
const store = createStore();
const listenerId = store.addCellListener(
  'pets',
  null,
  'color',
  (store, tableId, rowId, cellId, color) => {
    if (!validColors.includes(color)) {
      store.setCell(tableId, rowId, cellId, validColors[0]);
    }
  },
  true,
);

store.setRow('pets', 'fido', {species: 'dog', color: 'honey'});
console.log(store.getRow('pets', 'fido'));
// -> {species: 'dog', color: 'walnut'}

validColors.shift();
console.log(validColors);
// -> ['brown', 'black']

store.callListener(listenerId);
console.log(store.getRow('pets', 'fido'));
// -> {species: 'dog', color: 'brown'}

store.delListener(listenerId);

This example registers a listener to Row Id changes. It is explicitly called and fires for two Tables in the Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});

const listenerId = store.addRowIdsListener(null, (store, tableId) => {
  console.log(`Row Ids listener called for ${tableId} table`);
});

store.callListener(listenerId);
// -> 'Row Ids listener called for pets table'
// -> 'Row Ids listener called for species table'

store.delListener(listenerId);

This example registers a listener Value changes. It is explicitly called and fires for two Values in the Store.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});

const listenerId = store.addValueListener(
  null,
  (store, valueId, value) => {
    console.log(`Value listener called for ${valueId} value, ${value}`);
  },
);

store.callListener(listenerId);
// -> 'Value listener called for open value, true'
// -> 'Value listener called for employees value, 3'

store.delListener(listenerId);

This example registers listeners for the end of transactions, and for invalid Cells. They are explicitly called, meaninglessly. The former receives empty arguments. The latter is not called at all.

import {createStore} from 'tinybase';

const store = createStore();

const listenerId = store.addWillFinishTransactionListener(
  (store, cellsTouched, valuesTouched) => {
    console.log(`Transaction finish: ${cellsTouched}/${valuesTouched}`);
  },
);
store.callListener(listenerId);
// -> 'Transaction finish: undefined/undefined'
store.delListener(listenerId);

const listenerId2 = store.addInvalidCellListener(
  null,
  null,
  null,
  (store, tableId, rowId, cellId) => {
    console.log('Invalid cell', tableId, rowId, cellId);
  },
);
store.callListener(listenerId2);
// -> undefined
store.delListener(listenerId2);
Since

v1.0.0

delListener

The delListener method removes a listener that was previously added to the Store.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Store.

Use the Id returned by whichever method was used to add the listener. Note that the Store may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
const listenerId = store.addTablesListener(() => {
  console.log('Tables changed');
});

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Tables changed'

store.delListener(listenerId);

store.setCell('pets', 'fido', 'color', 'honey');
// -> undefined
// The listener is not called.
Since

v1.0.0

Iterator methods

This is the collection of iterator methods within the MergeableStore interface. There are 5 iterator methods in total.

forEachTable

The forEachTable method takes a function that it will then call for each Table in the Store.

forEachTable(tableCallback: TableCallback): void
TypeDescription
tableCallbackTableCallback

The function that should be called for every Table.

returnsvoid

This has no return value.

This method is useful for iterating over the Table structure of the Store in a functional style. The tableCallback parameter is a TableCallback function that will be called with the Id of each Table, and with a function that can then be used to iterate over each Row of the Table, should you wish.

Example

This example iterates over each Table in a Store, and lists each Row Id within them.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
store.forEachTable((tableId, forEachRow) => {
  console.log(tableId);
  forEachRow((rowId) => console.log(`- ${rowId}`));
});
// -> 'pets'
// -> '- fido'
// -> 'species'
// -> '- dog'
Since

v1.0.0

forEachTableCell

The forEachTableCell method takes a function that it will then call for each Cell used across the whole Table.

forEachTableCell(
  tableId: string,
  tableCellCallback: TableCellCallback,
): void
TypeDescription
tableIdstring

The Id of the Table containing the Cells to iterate over.

tableCellCallbackTableCellCallback

The function that should be called for every Cell Id used across the whole Table.

returnsvoid

This has no return value.

This method is useful for iterating over the Cell structure of the Table in a functional style. The tableCellCallback parameter is a TableCellCallback function that will be called with the Id of each Cell and the count of Rows in the Table in which it appears.

Example

This example iterates over each Cell Id used across the whole Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}, felix: {species: 'cat', legs: 4}},
});
store.forEachTableCell('pets', (cellId, count) => {
  console.log(`${cellId}: ${count}`);
});
// -> 'species: 2'
// -> 'legs: 1'
Since

v3.3.0

forEachRow

The forEachRow method takes a function that it will then call for each Row in a specified Table.

forEachRow(
  tableId: string,
  rowCallback: RowCallback,
): void
TypeDescription
tableIdstring

The Id of the Table to iterate over.

rowCallbackRowCallback

The function that should be called for every Row.

returnsvoid

This has no return value.

This method is useful for iterating over the Row structure of the Table in a functional style. The rowCallback parameter is a RowCallback function that will be called with the Id of each Row, and with a function that can then be used to iterate over each Cell of the Row, should you wish.

Example

This example iterates over each Row in a Table, and lists each Cell Id within them.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {color: 'black'},
  },
});
store.forEachRow('pets', (rowId, forEachCell) => {
  console.log(rowId);
  forEachCell((cellId) => console.log(`- ${cellId}`));
});
// -> 'fido'
// -> '- species'
// -> 'felix'
// -> '- color'
Since

v1.0.0

forEachCell

The forEachCell method takes a function that it will then call for each Cell in a specified Row.

forEachCell(
  tableId: string,
  rowId: string,
  cellCallback: CellCallback,
): void
TypeDescription
tableIdstring

The Id of the Table containing the Row to iterate over.

rowIdstring

The Id of the Row to iterate over.

cellCallbackCellCallback

The function that should be called for every Cell.

returnsvoid

This has no return value.

This method is useful for iterating over the Cell structure of the Row in a functional style. The cellCallback parameter is a CellCallback function that will be called with the Id and value of each Cell.

Example

This example iterates over each Cell in a Row, and lists its value.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', color: 'brown'}},
});
store.forEachCell('pets', 'fido', (cellId, cell) => {
  console.log(`${cellId}: ${cell}`);
});
// -> 'species: dog'
// -> 'color: brown'
Since

v1.0.0

forEachValue

The forEachValue method takes a function that it will then call for each Value in a Store.

forEachValue(valueCallback: ValueCallback): void
TypeDescription
valueCallbackValueCallback

The function that should be called for every Value.

returnsvoid

This has no return value.

This method is useful for iterating over the Value structure of the Store in a functional style. The valueCallback parameter is a ValueCallback function that will be called with the Id and value of each Value.

Example

This example iterates over each Value in a Store, and lists its value.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
store.forEachValue((valueId, value) => {
  console.log(`${valueId}: ${value}`);
});
// -> 'open: true'
// -> 'employees: 3'
Since

v3.0.0

Syncing methods

This is the collection of syncing methods within the MergeableStore interface. There are 9 syncing methods in total.

getMergeableTableDiff

The getMergeableTableDiff method returns information about new and differing Table objects of a MergeableStore relative to another.

getMergeableTableDiff(otherTableHashes: TableHashes): [newTables: [thing: {[tableId: Id]: TableStamp<Hashed>}, time?: string], differingTableHashes: TableHashes]
TypeDescription
otherTableHashesTableHashes

The TableHashes of another MergeableStore.

returns[newTables: [thing: {[tableId: Id]: TableStamp<Hashed>}, time?: string], differingTableHashes: TableHashes]

A pair of objects describing the new and differing Table objects of this MergeableStore relative to the other.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates two MergeableStores, sets some differing data, and then identifies the differences in the Table objects of one versus the other. Once they have been merged, the differences are empty.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setTables({pets: {fido: {color: 'brown'}}});

const store2 = createMergeableStore('store2');
store2.setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});

console.log(
  store2.getMergeableTableDiff(store1.getMergeableTableHashes()),
);
// ->
[
  [{species: [{dog: [{price: [5, 'Nn1JUF----0CnH-J']}]}]}],
  {pets: 1212600658},
];

store1.merge(store2);

console.log(
  store2.getMergeableTableDiff(store1.getMergeableTableHashes()),
);
// -> [[{}], {}]
Since

v5.0.0

getMergeableTableHashes

The getMergeableTableHashes method returns hashes for the Table objects in a MergeableStore.

getMergeableTableHashes(): TableHashes
returnsTableHashes

A TableHashes object with the hashes of each Table in the MergeableStore.

If two Table Ids have different hashes, that indicates that the content within them is different and should be synchronized.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates a MergeableStore, sets some data, and then accesses the Table hashes.

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1');

store.setCell('pets', 'fido', 'color', 'brown');
console.log(store.getMergeableTableHashes());
// -> {pets: 518810247}

store.setCell('species', 'dog', 'price', 5);
console.log(store.getMergeableTableHashes());
// -> {pets: 518810247, species: 2324343240}
Since

v5.0.0

getMergeableRowDiff

The getMergeableRowDiff method returns information about new and differing Row objects of a MergeableStore relative to another.

getMergeableRowDiff(otherTableRowHashes: RowHashes): [newRows: [thing: {[tableId: Id]: TableStamp<Hashed>}, time?: string], differingRowHashes: RowHashes]
TypeDescription
otherTableRowHashesRowHashes

The RowHashes of another MergeableStore.

returns[newRows: [thing: {[tableId: Id]: TableStamp<Hashed>}, time?: string], differingRowHashes: RowHashes]

A pair of objects describing the new and differing Row objects of this MergeableStore relative to the other.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates two MergeableStores, sets some differing data, and then identifies the differences in the Row objects of one versus the other. Once they have been merged, the differences are empty.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setTables({pets: {fido: {color: 'brown'}}});

const store2 = createMergeableStore('store2');
store2.setTables({pets: {fido: {color: 'black'}, felix: {color: 'tan'}}});

console.log(
  store2.getMergeableRowDiff(
    store1.getMergeableRowHashes(
      store2.getMergeableTableDiff(store1.getMergeableTableHashes())[1],
    ),
  ),
);
// ->
[
  [{pets: [{felix: [{color: ['tan', 'Nn1JUF----0CnH-J']}]}]}],
  {pets: {fido: 1038491054}},
];

store1.merge(store2);

console.log(
  store2.getMergeableRowDiff(
    store1.getMergeableRowHashes(
      store2.getMergeableTableDiff(store1.getMergeableTableHashes())[1],
    ),
  ),
);
// -> [[{}], {}]
Since

v5.0.0

getMergeableRowHashes

The getMergeableRowHashes method returns hashes for Row objects in a MergeableStore.

getMergeableRowHashes(otherTableHashes: TableHashes): RowHashes
TypeDescription
otherTableHashesTableHashes

The TableHashes from the other MergeableStore so that the differences can be efficiently identified.

returnsRowHashes

A RowHashes object with the hashes of each Row in the relevant Table objects of the MergeableStore.

If two Row Ids have different hashes, that indicates that the content within them is different and should be synchronized.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates a MergeableStore, sets some data, and then accesses the Row hashes for the differing Table Ids.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setTables({pets: {fido: {color: 'brown'}, felix: {color: 'tan'}}});

const store2 = createMergeableStore('store2');
store2.setTables({pets: {fido: {color: 'black'}, felix: {color: 'tan'}}});

console.log(
  store1.getMergeableRowHashes(
    store2.getMergeableTableDiff(store1.getMergeableTableHashes())[1],
  ),
);
// -> {pets: {felix: 1683761402, fido: 851131566}}
Since

v5.0.0

getMergeableCellDiff

The getMergeableCellDiff method returns information about new and differing Cell objects of a MergeableStore relative to another.

getMergeableCellDiff(otherTableRowCellHashes: CellHashes): [thing: {[tableId: Id]: TableStamp<Hashed>}, time?: string]
TypeDescription
otherTableRowCellHashesCellHashes

The CellHashes of another MergeableStore.

returns[thing: {[tableId: Id]: TableStamp<Hashed>}, time?: string]

The new and differing Cell objects of this MergeableStore relative to the other.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates two MergeableStores, sets some differing data, and then identifies the differences in the Cell objects of one versus the other. Once they have been merged, the differences are empty.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setTables({pets: {fido: {color: 'brown'}}});

const store2 = createMergeableStore('store2');
store2.setTables({pets: {fido: {color: 'black', species: 'dog'}}});

console.log(
  store2.getMergeableCellDiff(
    store1.getMergeableCellHashes(
      store2.getMergeableRowDiff(
        store1.getMergeableRowHashes(
          store2.getMergeableTableDiff(
            store1.getMergeableTableHashes(),
          )[1],
        ),
      )[1],
    ),
  ),
);
// ->
[
  {
    pets: [
      {
        fido: [
          {
            color: ['black', 'Nn1JUF-----CnH-J'],
            species: ['dog', 'Nn1JUF----0CnH-J'],
          },
        ],
      },
    ],
  },
];

store1.merge(store2);

console.log(
  store2.getMergeableCellDiff(
    store1.getMergeableCellHashes(
      store2.getMergeableRowDiff(
        store1.getMergeableRowHashes(
          store2.getMergeableTableDiff(
            store1.getMergeableTableHashes(),
          )[1],
        ),
      )[1],
    ),
  ),
);
// -> [{}]
Since

v5.0.0

getMergeableCellHashes

The getMergeableCellHashes method returns hashes for Cell objects in a MergeableStore.

getMergeableCellHashes(otherTableRowHashes: RowHashes): CellHashes
TypeDescription
otherTableRowHashesRowHashes

The RowHashes from the other MergeableStore so that the differences can be efficiently identified.

returnsCellHashes

A CellHashes object with the hashes of each Cell in the relevant Row objects of the MergeableStore.

If two Cell Ids have different hashes, that indicates that the content within them is different and should be synchronized.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates a MergeableStore, sets some data, and then accesses the Cell hashes for the differing Table Ids.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setTables({pets: {fido: {color: 'brown', species: 'dog'}}});

const store2 = createMergeableStore('store2');
store2.setTables({pets: {fido: {color: 'black', species: 'dog'}}});

console.log(
  store1.getMergeableCellHashes(
    store2.getMergeableRowDiff(
      store1.getMergeableRowHashes(
        store2.getMergeableTableDiff(store1.getMergeableTableHashes())[1],
      ),
    )[1],
  ),
);
// -> {pets: {fido: {color: 923684530, species: 227729753}}}
Since

v5.0.0

getMergeableValueDiff

The getMergeableValueDiff method returns information about new and differing Value objects of a MergeableStore relative to another.

getMergeableValueDiff(otherValueHashes: ValueHashes): [thing: {[valueId: Id]: ValueStamp<Hashed>}, time?: string]
TypeDescription
otherValueHashesValueHashes

The ValueHashes of another MergeableStore.

returns[thing: {[valueId: Id]: ValueStamp<Hashed>}, time?: string]

The new and differing Value objects of this MergeableStore relative to the other.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates two MergeableStores, sets some differing data, and then identifies the differences in the Value objects of one versus the other. Once they have been merged, the differences are empty.

import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore('store1');
store1.setValues({employees: 3});

const store2 = createMergeableStore('store2');
store2.setValues({employees: 4, open: true});

console.log(
  store2.getMergeableValueDiff(store1.getMergeableValueHashes()),
);
// ->
[
  {
    employees: [4, 'Nn1JUF-----CnH-J'],
    open: [true, 'Nn1JUF----0CnH-J'],
  },
];

store1.merge(store2);

console.log(
  store2.getMergeableValueDiff(store1.getMergeableValueHashes()),
);
// -> [{}]
Since

v5.0.0

getMergeableValueHashes

The getMergeableValueHashes method returns hashes for the Value objects in a MergeableStore.

getMergeableValueHashes(): ValueHashes
returnsValueHashes

A ValueHashes object with the hashes of each Value in the MergeableStore.

If two Value Ids have different hashes, that indicates that the content within them is different and should be synchronized.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates a MergeableStore, sets some data, and then accesses the Value hashes.

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1');

store.setValue('employees', 3);
console.log(store.getMergeableValueHashes());
// -> {employees: 1940815977}

store.setValue('open', true);
console.log(store.getMergeableValueHashes());
// -> {employees: 1940815977, open: 3860530645}
Since

v5.0.0

getMergeableContentHashes

The getMergeableContentHashes method returns hashes for the full content of a MergeableStore.

getMergeableContentHashes(): ContentHashes
returnsContentHashes

A ContentHashes array for the hashes of the full content of the MergeableStore.

If two MergeableStore instances have different hashes, that indicates that the mergeable Tables or Values within them are different and should be synchronized.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example creates a MergeableStore, sets some data, and then accesses the content hashes.

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1');

store.setCell('pets', 'fido', 'color', 'brown');
console.log(store.getMergeableContentHashes());
// -> [784336119, 0]

store.setValue('open', true);
console.log(store.getMergeableContentHashes());
// -> [784336119, 2829789038]
Since

v5.0.0

Transaction methods

This is the collection of transaction methods within the MergeableStore interface. There are 6 transaction methods in total.

finishTransaction

The finishTransaction method allows you to explicitly finish a transaction that has made multiple mutations to the Store, triggering all calls to the relevant listeners.

finishTransaction(doRollback?: DoRollback): this
TypeDescription
doRollback?DoRollback

An optional callback that should return true if you want to rollback the transaction at the end.

returnsthis

A reference to the Store.

Transactions are useful for making bulk changes to the data in a Store, and when you don't want listeners to be called as you make each change. Changes are made silently during the transaction, and listeners relevant to the changes you have made will instead only be called when the whole transaction is complete.

Generally it is preferable to use the transaction method to wrap a block of code as a transaction. It simply calls both the startTransaction and finishTransaction methods for you. See that method for several transaction examples.

Use this finishTransaction method when you have a more 'open-ended' transaction, such as one containing mutations triggered from other events that are asynchronous or not occurring inline to your code. There must have been a corresponding startTransaction method that this completes, of course, otherwise this function has no effect.

The optional parameter, doRollback is a DoRollback callback that you can use to rollback the transaction if it did not complete to your satisfaction. It is called with getTransactionChanges and getTransactionLog parameters, which inform you of the net changes that have been made during the transaction, at different levels of detail. See the DoRollback documentation for more details.

Examples

This example makes changes to two Cells, first outside, and secondly within, a transaction that is explicitly started and finished. In the second case, the Row listener is only called once.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
store.addRowListener('pets', 'fido', () => console.log('Fido changed'));

store
  .setCell('pets', 'fido', 'color', 'brown')
  .setCell('pets', 'fido', 'sold', false);
// -> 'Fido changed'
// -> 'Fido changed'

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'walnut')
  .setCell('pets', 'fido', 'sold', true)
  .finishTransaction();
// -> 'Fido changed'

This example makes multiple changes to the Store, including some attempts to update a Cell with invalid values. The doRollback callback receives information about the changes and invalid attempts, and then judges that the transaction should be rolled back to its original state.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'black')
  .setCell('pets', 'fido', 'eyes', ['left', 'right'])
  .setCell('pets', 'fido', 'info', {sold: null})
  .setValue('open', false)
  .setValue('employees', ['alice', 'bob'])
  .finishTransaction(() => {
    const [, , changedCells, invalidCells, changedValues, invalidValues] =
      store.getTransactionLog();
    console.log(store.getTables());
    console.log(changedCells);
    console.log(invalidCells);
    console.log(changedValues);
    console.log(invalidValues);
    return invalidCells['pets'] != null;
  });
// -> {pets: {fido: {species: 'dog', color: 'black'}}}
// -> {pets: {fido: {color: ['brown', 'black']}}}
// -> {pets: {fido: {eyes: [['left', 'right']], info: [{sold: null}]}}}
// -> {open: [true, false]}
// -> {employees: [['alice', 'bob']]}

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
console.log(store.getValues());
// -> {open: true}
Since

v1.3.0

getTransactionChanges

The getTransactionChanges method returns the net meaningful changes that have been made to a Store during a transaction.

getTransactionChanges(): Changes
returnsChanges

A Changes object representing the changes.

This is useful for deciding whether to rollback a transaction, for example. The returned object is only meaningful if the method is called when the Store is in a transaction - such as in a TransactionListener.

Example

This example makes changes to the Store. At the end of the transaction, detail about what changed is enumerated.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'black')
  .setValue('open', false)
  .finishTransaction(() => {
    const [changedCells, changedValues] = store.getTransactionChanges();
    console.log(changedCells);
    console.log(changedValues);
  });
// -> {pets: {fido: {color: 'black'}}}
// -> {open: false}
Since

v5.0.0

getTransactionLog

The getTransactionLog method returns the changes that were made to a Store during a transaction in more detail, including invalid changes, and what previous values were.

getTransactionLog(): TransactionLog
returnsTransactionLog

A TransactionLog object representing the changes.

This is useful for deciding whether to rollback a transaction, for example. The returned object is only meaningful if the method is called when the Store is in a transaction - such as in a TransactionListener.

Example

This example makes changes to the Store. At the end of the transaction, detail about what changed is enumerated.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'black')
  .setCell('pets', 'fido', 'eyes', ['left', 'right'])
  .setCell('pets', 'fido', 'info', {sold: null})
  .setValue('open', false)
  .setValue('employees', ['alice', 'bob'])
  .finishTransaction(() => {
    const [, , changedCells, invalidCells, changedValues, invalidValues] =
      store.getTransactionLog();
    console.log(changedCells);
    console.log(invalidCells);
    console.log(changedValues);
    console.log(invalidValues);
  });
// -> {pets: {fido: {color: ['brown', 'black']}}}
// -> {pets: {fido: {eyes: [['left', 'right']], info: [{sold: null}]}}}
// -> {open: [true, false]}
// -> {employees: [['alice', 'bob']]}
Since

v5.0.0

getTransactionMergeableChanges

The getTransactionMergeableChanges method returns the net meaningful changes that have been made to a MergeableStore during a transaction.

getTransactionMergeableChanges(): MergeableChanges
returnsMergeableChanges

A MergeableChanges object representing the changes.

The method is generally intended to be used internally within TinyBase itself and the return type is assumed to be opaque to applications that use it.

Example

This example makes changes to the MergeableStore. At the end of the transaction, detail about what changed is enumerated.

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1');
store.setTables({pets: {fido: {species: 'dog', color: 'brown'}}});
store.setValues({open: true});

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'black')
  .setValue('open', false)
  .finishTransaction(() => {
    console.log(store.getTransactionMergeableChanges());
  });
// ->
[
  [{pets: [{fido: [{color: ['black', 'Nn1JUF----2FnHIC']}]}]}],
  [{open: [false, 'Nn1JUF----3FnHIC']}],
  1,
];
Since

v5.0.0

startTransaction

The startTransaction method allows you to explicitly start a transaction that will make multiple mutations to the Store, buffering all calls to the relevant listeners until it completes when you call the finishTransaction method.

startTransaction(): this
returnsthis

A reference to the Store.

Transactions are useful for making bulk changes to the data in a Store, and when you don't want listeners to be called as you make each change. Changes are made silently during the transaction, and listeners relevant to the changes you have made will instead only be called when the whole transaction is complete.

Generally it is preferable to use the transaction method to wrap a block of code as a transaction. It simply calls both the startTransaction and finishTransaction methods for you. See that method for several transaction examples.

Use this startTransaction method when you have a more 'open-ended' transaction, such as one containing mutations triggered from other events that are asynchronous or not occurring inline to your code. You must remember to also call the finishTransaction method explicitly when it is done, of course.

Example

This example makes changes to two Cells, first outside, and secondly within, a transaction that is explicitly started and finished. In the second case, the Row listener is only called once.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
store.addRowListener('pets', 'fido', () => console.log('Fido changed'));

store
  .setCell('pets', 'fido', 'color', 'brown')
  .setCell('pets', 'fido', 'sold', false);
// -> 'Fido changed'
// -> 'Fido changed'

store
  .startTransaction()
  .setCell('pets', 'fido', 'color', 'walnut')
  .setCell('pets', 'fido', 'sold', true)
  .finishTransaction();
// -> 'Fido changed'
Since

v1.3.0

transaction

The transaction method takes a function that makes multiple mutations to the Store, buffering all calls to the relevant listeners until it completes.

transaction<Return>(
  actions: () => Return,
  doRollback?: DoRollback,
): Return
TypeDescription
actions() => Return

The function to be executed as a transaction.

doRollback?DoRollback

An optional callback that should return true if you want to rollback the transaction at the end. Since v1.2.

returnsReturn

Whatever value the provided transaction function returns.

This method is useful for making bulk changes to the data in a Store, and when you don't want listeners to be called as you make each change. Changes are made silently during the transaction, and listeners relevant to the changes you have made will instead only be called when the whole transaction is complete.

If multiple changes are made to a piece of Store data throughout the transaction, a relevant listener will only be called with the final value (assuming it is different to the value at the start of the transaction), regardless of the changes that happened in between. For example, if a Cell had a value 'a' and then, within a transaction, it was changed to 'b' and then 'c', any CellListener registered for that cell would be called once as if there had been a single change from 'a' to 'c'.

Transactions can be nested. Relevant listeners will be called only when the outermost one completes.

The second, optional parameter, doRollback is a DoRollback callback that you can use to rollback the transaction if it did not complete to your satisfaction. See the DoRollback documentation for more details.

Examples

This example makes changes to two Cells, first outside, and secondly within, a transaction. In the second case, the Row listener is only called once.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
store.addRowListener('pets', 'fido', () => console.log('Fido changed'));

store
  .setCell('pets', 'fido', 'color', 'brown')
  .setCell('pets', 'fido', 'sold', false);
// -> 'Fido changed'
// -> 'Fido changed'

store.transaction(() =>
  store
    .setCell('pets', 'fido', 'color', 'walnut')
    .setCell('pets', 'fido', 'sold', true),
);
// -> 'Fido changed'

This example makes multiple changes to one Cell. The Cell listener is called once - and with the final value - only if there is a net overall change.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
store.addCellListener(
  'pets',
  'fido',
  'color',
  (store, tableId, rowId, cellId, newCell) => console.log(newCell),
);

store.transaction(() =>
  store
    .setCell('pets', 'fido', 'color', 'black')
    .setCell('pets', 'fido', 'color', 'brown')
    .setCell('pets', 'fido', 'color', 'walnut'),
);
// -> 'walnut'

store.transaction(() =>
  store
    .setCell('pets', 'fido', 'color', 'black')
    .setCell('pets', 'fido', 'color', 'walnut'),
);
// -> undefined
// No net change during the transaction, so the listener is not called.

This example makes multiple changes to the Store, including some attempts to update a Cell and Value with invalid values. The doRollback callback receives information about the changes and invalid attempts, and then judges that the transaction should be rolled back to its original state.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {species: 'dog', color: 'brown'}}})
  .setValues({open: true});

store.transaction(
  () =>
    store
      .setCell('pets', 'fido', 'color', 'black')
      .setCell('pets', 'fido', 'eyes', ['left', 'right'])
      .setCell('pets', 'fido', 'info', {sold: null})
      .setValue('open', false)
      .setValue('employees', ['alice', 'bob']),
  () => {
    const [, , changedCells, invalidCells, changedValues, invalidValues] =
      store.getTransactionLog();
    console.log(store.getTables());
    console.log(changedCells);
    console.log(invalidCells);
    console.log(changedValues);
    console.log(invalidValues);
    return invalidCells['pets'] != null;
  },
);
// -> {pets: {fido: {species: 'dog', color: 'black'}}}
// -> {pets: {fido: {color: ['brown', 'black']}}}
// -> {pets: {fido: {eyes: [['left', 'right']], info: [{sold: null}]}}}
// -> {open: [true, false]}
// -> {employees: [['alice', 'bob']]}

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
console.log(store.getValues());
// -> {open: true}
Since

v1.0.0

Deleter methods

This is the collection of deleter methods within the MergeableStore interface. There are 9 deleter methods in total.

delTables

The delTables method lets you remove all of the data in a Store.

delTables(): this
returnsthis

A reference to the Store.

Example

This example removes the data of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

store.delTables();
console.log(store.getTables());
// -> {}
Since

v1.0.0

delTablesSchema

The delTablesSchema method lets you remove the TablesSchema of the Store.

delTablesSchema(): this
returnsthis

A reference to the Store.

Example

This example removes the TablesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore().setTablesSchema({
  pets: {species: {type: 'string'}},
});
store.delTablesSchema();
console.log(store.getTablesSchemaJson());
// -> '{}'
Since

v1.0.0

delTable

The delTable method lets you remove a single Table from the Store.

delTable(tableId: string): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

returnsthis

A reference to the Store.

Example

This example removes a Table from a Store.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
store.delTable('pets');

console.log(store.getTables());
// -> {species: {dog: {price: 5}}}
Since

v1.0.0

delRow

The delRow method lets you remove a single Row from a Table.

delRow(
  tableId: string,
  rowId: string,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

returnsthis

A reference to the Store.

If this is the last Row in its Table, then that Table will be removed.

Example

This example removes a Row from a Table.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}, felix: {species: 'cat'}},
});
store.delRow('pets', 'fido');

console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}
Since

v1.0.0

delCell

The delCell method lets you remove a single Cell from a Row.

delCell(
  tableId: string,
  rowId: string,
  cellId: string,
  forceDel?: boolean,
): this
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

cellIdstring

The Id of the Cell in the Row.

forceDel?boolean

An optional flag to indicate that the whole Row should be deleted, even if a TablesSchema provides a default value for this Cell. Defaults to false.

returnsthis

A reference to the Store.

When there is no TablesSchema applied to the Store, then if this is the last Cell in its Row, then that Row will be removed. If, in turn, that is the last Row in its Table, then that Table will be removed.

If there is a TablesSchema applied to the Store and it specifies a default value for this Cell, then deletion will result in it being set back to its default value. To override this, use the forceDel parameter, as described below.

The forceDel parameter is an optional flag that is only relevant if a TablesSchema provides a default value for this Cell. Under such circumstances, deleting a Cell value will normally restore it to the default value. If this flag is set to true, the complete removal of the Cell is instead guaranteed. But since doing do so would result in an invalid Row (according to the TablesSchema), in fact the whole Row is deleted to retain the integrity of the Table. Therefore, this flag should be used with caution.

Examples

This example removes a Cell from a Row without a TablesSchema.

import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {fido: {species: 'dog', sold: true}},
});
store.delCell('pets', 'fido', 'sold');

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

This example removes a Cell from a Row with a TablesSchema that defaults its value.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', sold: true}},
  })
  .setTablesSchema({
    pets: {
      species: {type: 'string'},
      sold: {type: 'boolean', default: false},
    },
  });
store.delCell('pets', 'fido', 'sold');

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', sold: false}}}

This example removes a Cell from a Row with a TablesSchema that defaults its value, but uses the forceDel parameter to override it.

import {createStore} from 'tinybase';

const store = createStore()
  .setTables({
    pets: {fido: {species: 'dog', sold: true}, felix: {species: 'cat'}},
  })
  .setTablesSchema({
    pets: {
      species: {type: 'string'},
      sold: {type: 'boolean', default: false},
    },
  });
store.delCell('pets', 'fido', 'sold', true);

console.log(store.getTables());
// -> {pets: {felix: {species: 'cat', sold: false}}}
Since

v1.0.0

delValues

The delValues method lets you remove all the Values from a Store.

delValues(): this
returnsthis

A reference to the Store.

If there is a ValuesSchema applied to the Store and it specifies a default value for any Value Id, then deletion will result in it being set back to its default value.

Examples

This example removes all Values from a Store without a ValuesSchema.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
store.delValues();

console.log(store.getValues());
// -> {}

This example removes all Values from a Store with a ValuesSchema that defaults one of its values.

import {createStore} from 'tinybase';

const store = createStore()
  .setValues({open: true, employees: 3})
  .setValuesSchema({
    open: {type: 'boolean', default: false},
    employees: {type: 'number'},
  });
store.delValues();

console.log(store.getValues());
// -> {open: false}
Since

v3.0.0

delValuesSchema

The delValuesSchema method lets you remove the ValuesSchema of the Store.

delValuesSchema(): this
returnsthis

A reference to the Store.

Example

This example removes the ValuesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore().setValuesSchema({
  sold: {type: 'boolean', default: false},
});
store.delValuesSchema();
console.log(store.getValuesSchemaJson());
// -> '{}'
Since

v3.0.0

delValue

The delValue method lets you remove a single Value from a Store.

delValue(valueId: string): this
TypeDescription
valueIdstring

The Id of the Value in the Row.

returnsthis

A reference to the Store.

If there is a ValuesSchema applied to the Store and it specifies a default value for this Value Id, then deletion will result in it being set back to its default value.

Examples

This example removes a Value from a Store without a ValuesSchema.

import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
store.delValue('employees');

console.log(store.getValues());
// -> {open: true}

This example removes a Value from a Store with a ValuesSchema that defaults its value.

import {createStore} from 'tinybase';

const store = createStore()
  .setValues({open: true, employees: 3})
  .setValuesSchema({
    open: {type: 'boolean', default: false},
    employees: {type: 'number'},
  });
store.delValue('open');

console.log(store.getValues());
// -> {open: false, employees: 3}
Since

v3.0.0

delSchema

The delSchema method lets you remove both the TablesSchema and ValuesSchema of the Store.

delSchema(): this
returnsthis

A reference to the Store.

Prior to v3.0, this method removed the TablesSchema only.

Example

This example removes the TablesSchema and ValuesSchema of a Store.

import {createStore} from 'tinybase';

const store = createStore()
  .setTablesSchema({
    pets: {species: {type: 'string'}},
  })
  .setValuesSchema({
    sold: {type: 'boolean', default: false},
  });
store.delSchema();
console.log(store.getSchemaJson());
// -> '[{},{}]'
Since

v3.0.0

Development methods

This is the collection of development methods within the MergeableStore interface. There is only one method, getListenerStats.

getListenerStats

The getListenerStats method provides a set of statistics about the listeners registered with the Store, and is used for debugging purposes.

getListenerStats(): StoreListenerStats
returnsStoreListenerStats

A StoreListenerStats object containing Store listener statistics.

The StoreListenerStats object contains a breakdown of the different types of listener. Totals include both mutator and non-mutator listeners.

The method is intended to be used during development to ensure your application is not leaking listener registrations, for example.

Example

This example gets the listener statistics of a small and simple Store.

import {createStore} from 'tinybase';

const store = createStore();
store.addTablesListener(() => console.log('Tables changed'));
store.addRowIdsListener(() => console.log('Row Ids changed'));

const listenerStats = store.getListenerStats();
console.log(listenerStats.rowIds);
// -> 1
console.log(listenerStats.tables);
// -> 1
Since

v1.0.0

Properties

There is one property, isMergeable, within the MergeableStore interface.

isMergeable

This will always be false for a Store, and true for a MergeableStore.

Since

v5.0.0

Functions

There is one function, createMergeableStore, within the mergeable-store module.

createMergeableStore

The createMergeableStore function creates a MergeableStore, and is the main entry point into the mergeable-store module.

createMergeableStore(uniqueId?: string): MergeableStore
TypeDescription
uniqueId?string
returnsMergeableStore

A reference to the new MergeableStore.

There is one optional parameter which is a uniqueId for the MergeableStore. This is used to distinguish conflicting changes made in the same millisecond by two different MergeableStore objects as its hash is added to the end of the HLC timestamps. Generally this can be omitted unless you have a need for deterministic HLCs, such as in a testing scenario. Otherwise, TinyBase will assign a unique Id to the Store at the time of creation.

Examples

This example creates a MergeableStore.

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1');

console.log(store.getContent());
// -> [{}, {}]
console.log(store.getMergeableContent());
// -> [[{}, '', 0], [{}, '', 0]]

This example creates a MergeableStore with some initial data:

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1').setTables({
  pets: {fido: {species: 'dog'}},
});

console.log(store.getContent());
// -> [{pets: {fido: {species: 'dog'}}}, {}]
console.log(store.getMergeableContent());
// ->
[
  [
    {
      pets: [
        {
          fido: [
            {species: ['dog', 'Nn1JUF-----FnHIC', 290599168]},
            '',
            2682656941,
          ],
        },
        '',
        2102515304,
      ],
    },
    '',
    3506229770,
  ],
  [{}, '', 0],
];

This example creates a MergeableStore with some initial data and a TablesSchema:

import {createMergeableStore} from 'tinybase';

const store = createMergeableStore('store1')
  .setTables({pets: {fido: {species: 'dog'}}})
  .setTablesSchema({
    pets: {
      species: {type: 'string'},
      sold: {type: 'boolean', default: false},
    },
  });

console.log(store.getContent());
// -> [{pets: {fido: {sold: false, species: 'dog'}}}, {}]
console.log(store.getMergeableContent());
// ->
[
  [
    {
      pets: [
        {
          fido: [
            {
              sold: [false, 'Nn1JUF----2FnHIC', 2603026204],
              species: ['dog', 'Nn1JUF----1FnHIC', 2817056260],
            },
            '',
            2859424112,
          ],
        },
        '',
        1640515891,
      ],
    },
    '',
    2077041985,
  ],
  [{}, '', 0],
];
Since

v5.0.0

Type Aliases

These are the type aliases within the mergeable-store module.

Mergeable type aliases

This is the collection of mergeable type aliases within the mergeable-store module. There are only two mergeable type aliases, MergeableChanges and MergeableContent.

MergeableChanges

The MergeableChanges type represents changes to the content of a MergeableStore and the metadata about that content) required to merge it with another.

[mergeableTables: TablesStamp, mergeableValues: ValuesStamp, isChanges: 1]

It is simply an array of two Stamp types, one for changes to the MergeableStore's Tables and one for changes to its Values. A final 1 is used to distinguish it from a full MergeableContent object.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

MergeableContent

The MergeableContent type represents the content of a MergeableStore and the metadata about that content) required to merge it with another.

[mergeableTables: TablesStamp<true>, mergeableValues: ValuesStamp<true>]

It is simply an array of two Stamp types, one for the MergeableStore's Tables and one for its Values.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

Stamps type aliases

This is the collection of stamps type aliases within the mergeable-store module. There are 9 stamps type aliases in total.

TablesStamp

The TablesStamp type is used as metadata to decide how to merge two different sets of Tables together.

Stamp<{[tableId: Id]: TableStamp<Hashed>}, Hashed>

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

TableStamp

The TableStamp type is used as metadata to decide how to merge two different Table objects together.

Stamp<{[rowId: Id]: RowStamp<Hashed>}, Hashed>

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

RowStamp

The RowStamp type is used as metadata to decide how to merge two different Row objects together.

Stamp<{[cellId: Id]: CellStamp<Hashed>}, Hashed>

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

CellStamp

The CellStamp type is used as metadata to decide how to merge two different Cell objects together.

Stamp<CellOrUndefined, Hashed>

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

ValuesStamp

The ValuesStamp type is used as metadata to decide how to merge two different sets of Values together.

Stamp<{[valueId: Id]: ValueStamp<Hashed>}, Hashed>

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

ValueStamp

The ValueStamp type is used as metadata to decide how to merge two different Value objects together.

Stamp<ValueOrUndefined, Hashed>

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

Hash

The Hash type is used within the mergeable-store module to quickly compare the content of two objects.

number

This is simply an alias for a JavaScript number.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

Stamp

The Stamp type is used as metadata to decide how to merge two different MergeableStore objects together.

Hashed extends true ? [thing: Thing, time: Time, hash: Hash] : [thing: Thing, time?: Time]

It describes a combination of a value (or object), a Time, and optionally a Hash, all in an array.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

Time

The Time type is used within the mergeable-store module to store the value of a hybrid logical clock (HLC).

string

It is simply an alias for a JavaScript string, but it comprises three HLC parts: a logical timestamp, a sequence counter, and a client Id. It is designed to be string-sortable and unique across all of the systems involved in synchronizing a MergeableStore.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

Syncing type aliases

This is the collection of syncing type aliases within the mergeable-store module. There are 5 syncing type aliases in total.

TableHashes

The TableHashes type is used to quickly compare the content of two sets of Table objects.

{[tableId: Id]: Hash}

It is simply an object of Hash types, one for each Table Id in the MergeableStore.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

RowHashes

The RowHashes type is used to quickly compare the content of two sets of Row objects.

{[tableId: Id]: {[rowId: Id]: Hash}}

It is simply a nested object of Hash types, one for each Row Id, for each TableId, in the MergeableStore.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

CellHashes

The CellHashes type is used to quickly compare the content of two sets of Cell objects.

{[tableId: Id]: {[rowId: Id]: {[cellId: Id]: Hash}}}

It is simply a nested object of Hash types, one for each Cell Id, for each Row Id, for each TableId, in the MergeableStore.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

ValueHashes

The ValueHashes type is used to quickly compare the content of two sets of Value objects.

{[valueId: Id]: Hash}

It is simply an object of Hash types, one for each Value Id in the MergeableStore.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

ContentHashes

The ContentHashes type is used to quickly compare the content of two MergeableStore objects.

[tablesHash: Hash, valuesHash: Hash]

It is simply an array of two Hash types, one for the MergeableStore's Tables and one for its Values.

This type is mostly utilized internally within TinyBase itself and is generally assumed to be opaque to applications that use it.

Since

v5.0.0

metrics

The metrics module of the TinyBase project provides the ability to create and track metrics and aggregates of the data in Store objects.

The main entry point to this module is the createMetrics function, which returns a new Metrics object. From there, you can create new Metric definitions, access the values of those Metrics directly, and register listeners for when they change.

Since

v1.0.0

Interfaces

There is one interface, Metrics, within the metrics module.

Metrics

A Metrics object lets you define, query, and listen to, aggregations of Cell values within a Table in a Store.

This is useful for counting the number of Row objects in a Table, averaging Cell values, or efficiently performing any arbitrary aggregations.

Create a Metrics object easily with the createMetrics function. From there, you can add new Metric definitions (with the setMetricDefinition method), query their values (with the getMetric method), and add listeners for when they change (with the addMetricListener method).

This module provides a number of predefined and self-explanatory aggregations ('sum', 'avg', 'min', and 'max'), and defaults to counting Row objects when using the setMetricDefinition method. However, far more complex aggregations can be configured with custom functions.

Example

This example shows a very simple lifecycle of a Metrics object: from creation, to adding a definition, getting a Metric, and then registering and removing a listener for it.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition(
  'highestPrice', // metricId
  'species', //      tableId to aggregate
  'max', //          aggregation
  'price', //        cellId to aggregate
);

console.log(metrics.getMetric('highestPrice'));
// -> 5

const listenerId = metrics.addMetricListener('highestPrice', () => {
  console.log(metrics.getMetric('highestPrice'));
});
store.setCell('species', 'horse', 'price', 20);
// -> 20

metrics.delListener(listenerId);
metrics.destroy();
See also
Since

v1.0.0

Getter methods

This is the collection of getter methods within the Metrics interface. There are 5 getter methods in total.

getStore

The getStore method returns a reference to the underlying Store that is backing this Metrics object.

getStore(): Store
returnsStore

A reference to the Store.

Example

This example creates a Metrics object against a newly-created Store and then gets its reference in order to update its data.

import {createMetrics, createStore} from 'tinybase';

const metrics = createMetrics(createStore());
metrics.setMetricDefinition('speciesCount', 'species');
metrics.getStore().setCell('species', 'dog', 'price', 5);
console.log(metrics.getMetric('speciesCount'));
// -> 1
Since

v1.0.0

getTableId

The getTableId method returns the Id of the underlying Table that is backing a Metric.

getTableId(metricId: string): undefined | string
TypeDescription
metricIdstring

The Id of a Metric.

returnsundefined | string

The Id of the Table backing the Metric, or undefined.

If the Metric Id is invalid, the method returns undefined.

Example

This example creates a Metrics object, a single Metric definition, and then queries it (and a non-existent definition) to get the underlying Table Id.

import {createMetrics, createStore} from 'tinybase';

const metrics = createMetrics(createStore());
metrics.setMetricDefinition('speciesCount', 'species');

console.log(metrics.getTableId('speciesCount'));
// -> 'species'
console.log(metrics.getTableId('petsCount'));
// -> undefined
Since

v1.0.0

getMetric

The getMetric method gets the current value of a Metric.

getMetric(metricId: string): undefined | number
TypeDescription
metricIdstring

The Id of the Metric.

returnsundefined | number

The numeric value of the Metric, or undefined.

If the identified Metric does not exist (or if the definition references a Table or Cell value that does not exist) then undefined is returned.

Example

This example creates a Store, creates a Metrics object, and defines a simple Metric to average the price values in the Table. It then uses getMetric to access its value (and also the value of a Metric that has not been defined).

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

console.log(metrics.getMetric('highestPrice'));
// -> 5
console.log(metrics.getMetric('lowestPrice'));
// -> undefined
Since

v1.0.0

getMetricIds

The getMetricIds method returns an array of the Metric Ids registered with this Metrics object.

getMetricIds(): Ids
returnsIds

An array of Ids.

Example

This example creates a Metrics object with two definitions, and then gets the Ids of the definitions.

import {createMetrics, createStore} from 'tinybase';

const metrics = createMetrics(createStore())
  .setMetricDefinition('speciesCount', 'species')
  .setMetricDefinition('petsCount', 'pets');

console.log(metrics.getMetricIds());
// -> ['speciesCount', 'petsCount']
Since

v1.0.0

hasMetric

The hasMetric method returns a boolean indicating whether a given Metric exists in the Metrics object, and has a value.

hasMetric(metricId: string): boolean
TypeDescription
metricIdstring

The Id of a possible Metric in the Metrics object.

returnsboolean

Whether a Metric with that Id exists.

Example

This example shows two simple Metric existence checks.

import {createMetrics, createStore} from 'tinybase';

const store = createStore();
const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

console.log(metrics.hasMetric('lowestPrice'));
// -> false
console.log(metrics.hasMetric('highestPrice'));
// -> false
store.setTable('species', {dog: {price: 5}, cat: {price: 4}});
console.log(metrics.hasMetric('highestPrice'));
// -> true
Since

v1.0.0

Listener methods

This is the collection of listener methods within the Metrics interface. There are only three listener methods, addMetricIdsListener, addMetricListener, and delListener.

addMetricIdsListener

The addMetricIdsListener method registers a listener function with the Metrics object that will be called whenever a Metric definition is added or removed.

addMetricIdsListener(listener: MetricIdsListener): string
TypeDescription
listenerMetricIdsListener

The function that will be called whenever a Metric definition is added or removed.

returnsstring

The provided listener is a MetricIdsListener function, and will be called with a reference to the Metrics object.

Example

This example creates a Store, a Metrics object, and then registers a listener that responds to the addition and the removal of a Metric definition.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
const listenerId = metrics.addMetricIdsListener((metrics) => {
  console.log(metrics.getMetricIds());
});

metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
// -> ['highestPrice']
metrics.delMetricDefinition('highestPrice');
// -> []

metrics.delListener(listenerId);
Since

v4.1.0

addMetricListener

The addMetricListener method registers a listener function with the Metrics object that will be called whenever the value of a specified Metric changes.

addMetricListener(
  metricId: IdOrNull,
  listener: MetricListener,
): string
TypeDescription
metricIdIdOrNull

The Id of the Metric to listen to, or null as a wildcard.

listenerMetricListener

The function that will be called whenever the Metric changes.

returnsstring

A unique Id for the listener that can later be used to remove it.

You can either listen to a single Metric (by specifying the Metric Id as the method's first parameter), or changes to any Metric (by providing a null wildcard).

The provided listener is a MetricListener function, and will be called with a reference to the Metrics object, the Id of the Metric that changed, the new Metric value, and the old Metric value.

Examples

This example creates a Store, a Metrics object, and then registers a listener that responds to any changes to a specific Metric.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

const listenerId = metrics.addMetricListener(
  'highestPrice',
  (metrics, _metricId, newMetric, oldMetric) => {
    console.log('highestPrice metric changed');
    console.log([oldMetric, newMetric]);
  },
);

store.setCell('species', 'horse', 'price', 20);
// -> 'highestPrice metric changed'
// -> [5, 20]

metrics.delListener(listenerId);

This example creates a Store, a Metrics object, and then registers a listener that responds to any changes to any Metric.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store)
  .setMetricDefinition('highestPrice', 'species', 'max', 'price')
  .setMetricDefinition('speciesCount', 'species');

const listenerId = metrics.addMetricListener(
  null,
  (metrics, metricId, newMetric, oldMetric) => {
    console.log(`${metricId} metric changed`);
    console.log([oldMetric, newMetric]);
  },
);

store.setCell('species', 'horse', 'price', 20);
// -> 'highestPrice metric changed'
// -> [5, 20]
// -> 'speciesCount metric changed'
// -> [3, 4]

metrics.delListener(listenerId);
Since

v1.0.0

delListener

The delListener method removes a listener that was previously added to the Metrics object.

delListener(listenerId: string): Metrics
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsMetrics

A reference to the Metrics object.

Use the Id returned by the addMetricListener method. Note that the Metrics object may re-use this Id for future listeners added to it.

Example

This example creates a Store, a Metrics object, registers a listener, and then removes it.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

const listenerId = metrics.addMetricListener('highestPrice', () => {
  console.log('highestPrice metric changed');
});

store.setCell('species', 'horse', 'price', 20);
// -> 'highestPrice metric changed'

metrics.delListener(listenerId);

store.setCell('species', 'giraffe', 'price', 50);
// -> undefined
// The listener is not called.
Since

v1.0.0

Configuration methods

This is the collection of configuration methods within the Metrics interface. There are only two configuration methods, delMetricDefinition and setMetricDefinition.

delMetricDefinition

The delMetricDefinition method removes an existing Metric definition.

delMetricDefinition(metricId: string): Metrics
TypeDescription
metricIdstring

The Id of the Metric to remove.

returnsMetrics

A reference to the Metrics object.

Example

This example creates a Store, creates a Metrics object, defines a simple Metric, and then removes it.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition('speciesCount', 'species');
console.log(metrics.getMetricIds());
// -> ['speciesCount']

metrics.delMetricDefinition('speciesCount');
console.log(metrics.getMetricIds());
// -> []
Since

v1.0.0

setMetricDefinition

The setMetricDefinition method lets you set the definition of a Metric.

setMetricDefinition(
  metricId: string,
  tableId: string,
  aggregate?: MetricAggregate | "sum" | "avg" | "min" | "max",
  getNumber?: string | (getCell: GetCell, rowId: string) => number,
  aggregateAdd?: MetricAggregateAdd,
  aggregateRemove?: MetricAggregateRemove,
  aggregateReplace?: MetricAggregateReplace,
): Metrics
TypeDescription
metricIdstring

The Id of the Metric to define.

tableIdstring

The Id of the Table the Metric will be calculated from.

aggregate?MetricAggregate | "sum" | "avg" | "min" | "max"

Either a string representing one of a set of common aggregation techniques ('sum', 'avg', 'min', or 'max'), or a function that aggregates numeric values from each Row to create the Metric's overall value. Defaults to 'sum'.

getNumber?string | (getCell: GetCell, rowId: string) => number

Either the Id of a Cell containing, or a function that produces, the numeric value that will be aggregated in the way specified by the aggregate parameter. Defaults to a function that returns 1 (meaning that if the aggregate and getNumber parameters are both omitted, the Metric will simply be a count of the Row objects in the Table).

aggregateAdd?MetricAggregateAdd

A function that can be used to optimize a custom MetricAggregate by providing a shortcut for when a single value is added to the input values - for example, when a Row is added to the Table.

aggregateRemove?MetricAggregateRemove

A function that can be used to optimize a custom MetricAggregate by providing a shortcut for when a single value is removed from the input values - for example ,when a Row is removed from the Table.

aggregateReplace?MetricAggregateReplace

A function that can be used to optimize a custom MetricAggregate by providing a shortcut for when a single value in the input values is replaced with another - for example, when a Row is updated.

returnsMetrics

A reference to the Metrics object.

Every Metric definition is identified by a unique Id, and if you re-use an existing Id with this method, the previous definition is overwritten.

A Metric is an aggregation of numeric values produced from each Row within a single Table. Therefore the definition must specify the Table (by its Id) to be aggregated.

Without the third aggregate parameter, the Metric will simply be a count of the number of Row objects in the Table. But often you will specify a more interesting aggregate - such as the four predefined aggregates, 'sum', 'avg', 'min', and 'max' - or a custom function that produces your own aggregation of an array of numbers.

The fourth getNumber parameter specifies which Cell in each Row contains the numerical values to be used in the aggregation. Alternatively, a custom function can be provided that produces your own numeric value from the local Row as a whole.

The final three parameters, aggregateAdd, aggregateRemove, aggregateReplace need only be provided when you are using your own custom aggregate function. These give you the opportunity to reduce your custom function's algorithmic complexity by providing shortcuts that can nudge an aggregation result when a single value is added, removed, or replaced in the input values.

Examples

This example creates a Store, creates a Metrics object, and defines a simple Metric to count the Row objects in the Table.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition('speciesCount', 'species');

console.log(metrics.getMetric('speciesCount'));
// -> 3

This example creates a Store, creates a Metrics object, and defines a standard Metric to get the highest value of each price Cell in the Row objects in the Table.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

console.log(metrics.getMetric('highestPrice'));
// -> 5

This example creates a Store, creates a Metrics object, and defines a custom Metric to get the lowest value of each price Cell, greater than 2.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition(
  'lowestPriceOver2',
  'species',
  (numbers) => Math.min(...numbers.filter((number) => number > 2)),
  'price',
);

console.log(metrics.getMetric('lowestPriceOver2'));
// -> 4

This example also creates a Store, creates a Metrics object, and defines a custom Metric to get the lowest value of each price Cell, greater than 2. However, it also reduces algorithmic complexity with two shortcut functions.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition(
  'lowestPriceOver2',
  'species',
  (numbers) => Math.min(...numbers.filter((number) => number > 2)),
  'price',
  (metric, add) => (add > 2 ? Math.min(metric, add) : metric),
  (metric, remove) => (remove == metric ? undefined : metric),
  (metric, add, remove) =>
    remove == metric
      ? undefined
      : add > 2
        ? Math.min(metric, add)
        : metric,
);

console.log(metrics.getMetric('lowestPriceOver2'));
// -> 4
store.setRow('species', 'fish', {price: 3});
console.log(metrics.getMetric('lowestPriceOver2'));
// -> 3

This example creates a Store, creates a Metrics object, and defines a custom Metric to get the average value of a discounted price.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5, discount: 0.3},
  cat: {price: 4, discount: 0.2},
  worm: {price: 1, discount: 0.2},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition(
  'averageDiscountedPrice',
  'species',
  'avg',
  (getCell) => getCell('price') * (1 - getCell('discount')),
);

console.log(metrics.getMetric('averageDiscountedPrice'));
// -> 2.5
Since

v1.0.0

Iterator methods

This is the collection of iterator methods within the Metrics interface. There is only one method, forEachMetric.

forEachMetric

The forEachMetric method takes a function that it will then call for each Metric in the Metrics object.

forEachMetric(metricCallback: MetricCallback): void
TypeDescription
metricCallbackMetricCallback

The function that should be called for every Metric.

returnsvoid

This has no return value.

This method is useful for iterating over all the Metrics in a functional style. The metricCallback parameter is a MetricCallback function that will be called with the Id of each Metric and its value.

Example

This example iterates over each Metric in a Metrics object.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});
const metrics = createMetrics(store)
  .setMetricDefinition('highestPrice', 'species', 'max', 'price')
  .setMetricDefinition('lowestPrice', 'species', 'min', 'price');

metrics.forEachMetric((metricId, metric) => {
  console.log([metricId, metric]);
});
// -> ['highestPrice', 5]
// -> ['lowestPrice', 1]
Since

v1.0.0

Lifecycle methods

This is the collection of lifecycle methods within the Metrics interface. There is only one method, destroy.

destroy

The destroy method should be called when this Metrics object is no longer used.

destroy(): void

This guarantees that all of the listeners that the object registered with the underlying Store are removed and it can be correctly garbage collected.

Example

This example creates a Store, adds a Metrics object with a definition (that registers a RowListener with the underlying Store), and then destroys it again, removing the listener.

import {createMetrics, createStore} from 'tinybase';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition('speciesCount', 'species');
console.log(store.getListenerStats().row);
// -> 1

metrics.destroy();

console.log(store.getListenerStats().row);
// -> 0
Since

v1.0.0

Development methods

This is the collection of development methods within the Metrics interface. There is only one method, getListenerStats.

getListenerStats

The getListenerStats method provides a set of statistics about the listeners registered with the Metrics object, and is used for debugging purposes.

getListenerStats(): MetricsListenerStats
returnsMetricsListenerStats

A MetricsListenerStats object containing Metrics listener statistics.

The method is intended to be used during development to ensure your application is not leaking listener registrations, for example.

Example

This example gets the listener statistics of a Metrics object.

import {createMetrics, createStore} from 'tinybase';

const store = createStore();
const metrics = createMetrics(store);
metrics.addMetricListener(null, () => console.log('Metric changed'));

console.log(metrics.getListenerStats());
// -> {metric: 1}
Since

v1.0.0

Functions

There is one function, createMetrics, within the metrics module.

createMetrics

The createMetrics function creates a Metrics object, and is the main entry point into the metrics module.

createMetrics(store: Store): Metrics
TypeDescription
storeStore

The Store for which to register Metric definitions.

returnsMetrics

A reference to the new Metrics object.

A given Store can only have one Metrics object associated with it. If you call this function twice on the same Store, your second call will return a reference to the Metrics object created by the first.

Examples

This example creates a Metrics object.

import {createMetrics, createStore} from 'tinybase';

const store = createStore();
const metrics = createMetrics(store);
console.log(metrics.getMetricIds());
// -> []

This example creates a Metrics object, and calls the method a second time for the same Store to return the same object.

import {createMetrics, createStore} from 'tinybase';

const store = createStore();
const metrics1 = createMetrics(store);
const metrics2 = createMetrics(store);
console.log(metrics1 === metrics2);
// -> true
Since

v1.0.0

Type Aliases

These are the type aliases within the metrics module.

Listener type aliases

This is the collection of listener type aliases within the metrics module. There are only two listener type aliases, MetricIdsListener and MetricListener.

MetricIdsListener

The MetricIdsListener type describes a function that is used to listen to Metric definitions being added or removed.

(metrics: Metrics): void
TypeDescription
metricsMetrics

A reference to the Metrics object that changed.

returnsvoid

This has no return value.

A MetricIdsListener is provided when using the addMetricIdsListener method. See that method for specific examples.

When called, a MetricIdsListener is given a reference to the Metrics object.

Since

v1.0.0

MetricListener

The MetricListener type describes a function that is used to listen to changes to a Metric.

(
  metrics: Metrics,
  metricId: Id,
  newMetric: Metric | undefined,
  oldMetric: Metric | undefined,
): void
TypeDescription
metricsMetrics

A reference to the Metrics object that changed.

metricIdId

The Id of the Metric that changed.

newMetricMetric | undefined

The new value of the Metric that changed.

oldMetricMetric | undefined

The old value of the Metric that changed.

returnsvoid

This has no return value.

A MetricListener is provided when using the addMetricListener method. See that method for specific examples.

When called, a MetricListener is given a reference to the Metrics object, the Id of the Metric that changed, and the new and old values of the Metric.

If this is the first time that a Metric has had a value (such as when a table has gained its first row), the old value will be undefined. If a Metric now no longer has a value, the new value will be undefined.

Since

v1.0.0

Aggregators type aliases

This is the collection of aggregators type aliases within the metrics module. There are 4 aggregators type aliases in total.

MetricAggregate

The MetricAggregate type describes a custom function that takes an array of numbers and returns an aggregate that is used as a Metric.

(
  numbers: number[],
  length: number,
): Metric
TypeDescription
numbersnumber[]

The array of numbers in the Metric's aggregation.

lengthnumber

The length of the array of numbers in the Metric's aggregation.

returnsMetric

There are a number of common predefined aggregators, such as for counting, summing, and averaging values. This type is instead used for when you wish to use a more complex aggregation of your own devising. See the setMetricDefinition method for more examples.

Since

v1.0.0

MetricAggregateAdd

The MetricAggregateAdd type describes a function that can be used to optimize a custom MetricAggregate by providing a shortcut for when a single value is added to the input values.

(
  metric: Metric,
  add: number,
  length: number,
): Metric | undefined
TypeDescription
metricMetric

The current value of the Metric.

addnumber

The number being added to the Metric's aggregation.

lengthnumber

The length of the array of numbers in the Metric's aggregation.

returnsMetric | undefined

Some aggregation functions do not need to recalculate the aggregation of the whole set when one value changes. For example, when adding a new number to a series, the new sum of the series is the new value added to the previous sum.

If it is not possible to shortcut the aggregation based on just one value being added, return undefined and the Metric will be completely recalculated.

When possible, if you are providing a custom MetricAggregate, seek an implementation of an MetricAggregateAdd function that can reduce the complexity cost of growing the input data set. See the setMetricDefinition method for more examples.

Since

v1.0.0

MetricAggregateRemove

The MetricAggregateRemove type describes a function that can be used to optimize a custom MetricAggregate by providing a shortcut for when a single value is removed from the input values.

(
  metric: Metric,
  remove: number,
  length: number,
): Metric | undefined
TypeDescription
metricMetric

The current value of the Metric.

removenumber

The number being removed from the Metric's aggregation.

lengthnumber

The length of the array of numbers in the Metric's aggregation.

returnsMetric | undefined

Some aggregation functions do not need to recalculate the aggregation of the whole set when one value changes. For example, when removing a number from a series, the new sum of the series is the new value subtracted from the previous sum.

If it is not possible to shortcut the aggregation based on just one value being removed, return undefined and the Metric will be completely recalculated. One example might be if you were taking the minimum of the values, and the previous minimum is being removed. The whole of the rest of the list will need to be re-scanned to find a new minimum.

When possible, if you are providing a custom MetricAggregate, seek an implementation of an MetricAggregateRemove function that can reduce the complexity cost of shrinking the input data set. See the setMetricDefinition method for more examples.

Since

v1.0.0

MetricAggregateReplace

The MetricAggregateReplace type describes a function that can be used to optimize a custom MetricAggregate by providing a shortcut for when a single value in the input values is replaced with another.

(
  metric: Metric,
  add: number,
  remove: number,
  length: number,
): Metric | undefined
TypeDescription
metricMetric

The current value of the Metric.

addnumber

The number being added to the Metric's aggregation.

removenumber

The number being removed from the Metric's aggregation.

lengthnumber

The length of the array of numbers in the Metric's aggregation.

returnsMetric | undefined

Some aggregation functions do not need to recalculate the aggregation of the whole set when one value changes. For example, when replacing a number in a series, the new sum of the series is the previous sum, plus the new value, minus the old value.

If it is not possible to shortcut the aggregation based on just one value changing, return undefined and the Metric will be completely recalculated.

When possible, if you are providing a custom MetricAggregate, seek an implementation of an MetricAggregateReplace function that can reduce the complexity cost of changing the input data set in place. See the setMetricDefinition method for more examples.

Since

v1.0.0

Callback type aliases

This is the collection of callback type aliases within the metrics module. There is only one type alias, MetricCallback.

MetricCallback

The MetricCallback type describes a function that takes a Metric's Id and a callback to loop over each Row within it.

(
  metricId: Id,
  metric?: Metric,
): void
TypeDescription
metricIdId

The Id of the Metric that the callback can operate on.

metric?Metric

The value of the Metric.

returnsvoid

This has no return value.

A MetricCallback is provided when using the forEachMetric method, so that you can do something based on every Metric in the Metrics object. See that method for specific examples.

Since

v1.0.0

Metric type aliases

This is the collection of metric type aliases within the metrics module. There is only one type alias, Metric.

Metric

The Metric type is simply an alias, but represents a number formed by aggregating multiple other numbers together.

number
Since

v1.0.0

Development type aliases

This is the collection of development type aliases within the metrics module. There is only one type alias, MetricsListenerStats.

MetricsListenerStats

The MetricsListenerStats type describes the number of listeners registered with the Metrics object, and can be used for debugging purposes.

{metric: number}
TypeDescription
metricnumber

The number of MetricListener functions registered with the Metrics object.

A MetricsListenerStats object is returned from the getListenerStats method.

Since

v1.0.0

indexes

The indexes module of the TinyBase project provides the ability to create and track indexes of the data in Store objects.

The main entry point to this module is the createIndexes function, which returns a new Indexes object. From there, you can create new Index definitions, access the contents of those Indexes directly, and register listeners for when they change.

Since

v1.0.0

Interfaces

There is one interface, Indexes, within the indexes module.

Indexes

An Indexes object lets you look up all the Row objects in a Table that have a certain Cell value.

This is useful for creating filtered views of a Table, or simple search functionality.

Create an Indexes object easily with the createIndexes function. From there, you can add new Index definitions (with the setIndexDefinition method), query their contents (with the getSliceIds method and getSliceRowIds method), and add listeners for when they change (with the addSliceIdsListener method and addSliceRowIdsListener method).

This module defaults to indexing Row objects by one of their Cell values. However, far more complex indexes can be configured with a custom function.

Example

This example shows a very simple lifecycle of an Indexes object: from creation, to adding a definition, getting its contents, and then registering and removing a listener for it.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition(
  'bySpecies', // indexId
  'pets', //      tableId to index
  'species', //   cellId to index
);

console.log(indexes.getSliceIds('bySpecies'));
// -> ['dog', 'cat']
console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
// -> ['fido', 'cujo']

const listenerId = indexes.addSliceIdsListener('bySpecies', () => {
  console.log(indexes.getSliceIds('bySpecies'));
});
store.setRow('pets', 'lowly', {species: 'worm'});
// -> ['dog', 'cat', 'worm']

indexes.delListener(listenerId);
indexes.destroy();
See also
Since

v1.0.0

Getter methods

This is the collection of getter methods within the Indexes interface. There are 7 getter methods in total.

getStore

The getStore method returns a reference to the underlying Store that is backing this Indexes object.

getStore(): Store
returnsStore

A reference to the Store.

Example

This example creates an Indexes object against a newly-created Store and then gets its reference in order to update its data.

import {createIndexes, createStore} from 'tinybase';

const indexes = createIndexes(createStore());
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
indexes.getStore().setCell('pets', 'fido', 'species', 'dog');
console.log(indexes.getSliceIds('bySpecies'));
// -> ['dog']
Since

v1.0.0

getTableId

The getTableId method returns the Id of the underlying Table that is backing an Index.

getTableId(indexId: string): undefined | string
TypeDescription
indexIdstring

The Id of an Index.

returnsundefined | string

The Id of the Table backing the Index, or undefined.

If the Index Id is invalid, the method returns undefined.

Example

This example creates an Indexes object, a single Index definition, and then queries it (and a non-existent definition) to get the underlying Table Id.

import {createIndexes, createStore} from 'tinybase';

const indexes = createIndexes(createStore());
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

console.log(indexes.getTableId('bySpecies'));
// -> 'pets'
console.log(indexes.getTableId('byColor'));
// -> undefined
Since

v1.0.0

getSliceRowIds

The getSliceRowIds method gets the list of Row Ids in a given Slice, within a given Index.

getSliceRowIds(
  indexId: string,
  sliceId: string,
): Ids
TypeDescription
indexIdstring

The Id of the Index.

sliceIdstring

The Id of the Slice in the Index.

returnsIds

The Row Ids in the Slice, or an empty array.

If the identified Index or Slice do not exist (or if the definition references a Table that does not exist) then an empty array is returned.

Example

This example creates a Store, creates an Indexes object, and defines a simple Index. It then uses getSliceRowIds to see the Row Ids in the Slice (and also the Row Ids in Slices that do not exist).

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
// -> ['fido', 'cujo']
console.log(indexes.getSliceRowIds('bySpecies', 'worm'));
// -> []
console.log(indexes.getSliceRowIds('byColor', 'brown'));
// -> []
Since

v1.0.0

getIndexIds

The getIndexIds method returns an array of the Index Ids registered with this Indexes object.

getIndexIds(): Ids
returnsIds

An array of Ids.

Example

This example creates an Indexes object with two definitions, and then gets the Ids of the definitions.

import {createIndexes, createStore} from 'tinybase';

const indexes = createIndexes(createStore())
  .setIndexDefinition('bySpecies', 'pets', 'species')
  .setIndexDefinition('byColor', 'pets', 'color');

console.log(indexes.getIndexIds());
// -> ['bySpecies', 'byColor']
Since

v1.0.0

getSliceIds

The getSliceIds method gets the list of Slice Ids in an Index.

getSliceIds(indexId: string): Ids
TypeDescription
indexIdstring

The Id of the Index.

returnsIds

The Slice Ids in the Index, or an empty array.

If the identified Index does not exist (or if the definition references a Table that does not exist) then an empty array is returned.

Example

This example creates a Store, creates an Indexes object, and defines a simple Index. It then uses getSliceIds to see the available Slice Ids in the Index (and also the Slice Ids in an Index that has not been defined).

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

console.log(indexes.getSliceIds('bySpecies'));
// -> ['dog', 'cat']
console.log(indexes.getSliceIds('byColor'));
// -> []
Since

v1.0.0

hasIndex

The hasIndex method returns a boolean indicating whether a given Index exists in the Indexes object.

hasIndex(indexId: string): boolean
TypeDescription
indexIdstring

The Id of a possible Index in the Indexes object.

returnsboolean

Whether an Index with that Id exists.

Example

This example shows two simple Index existence checks.

import {createIndexes, createStore} from 'tinybase';

const indexes = createIndexes(createStore());
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
console.log(indexes.hasIndex('bySpecies'));
// -> true
console.log(indexes.hasIndex('byColor'));
// -> false
Since

v1.0.0

hasSlice

The hasSlice method returns a boolean indicating whether a given Slice exists in the Indexes object.

hasSlice(
  indexId: string,
  sliceId: string,
): boolean
TypeDescription
indexIdstring

The Id of a possible Index in the Indexes object.

sliceIdstring

The Id of a possible Slice in the Index.

returnsboolean

Whether a Slice with that Id exists.

Example

This example shows two simple Index existence checks.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
console.log(indexes.hasSlice('bySpecies', 'dog'));
// -> true
console.log(indexes.hasSlice('bySpecies', 'worm'));
// -> false
Since

v1.0.0

Listener methods

This is the collection of listener methods within the Indexes interface. There are 4 listener methods in total.

addSliceRowIdsListener

The addSliceRowIdsListener method registers a listener function with the Indexes object that will be called whenever the Row Ids in a Slice change.

addSliceRowIdsListener(
  indexId: IdOrNull,
  sliceId: IdOrNull,
  listener: SliceRowIdsListener,
): string
TypeDescription
indexIdIdOrNull

The Id of the Index to listen to, or null as a wildcard.

sliceIdIdOrNull

The Id of the Slice to listen to, or null as a wildcard.

listenerSliceRowIdsListener

The function that will be called whenever the Row Ids in the Slice change.

returnsstring

A unique Id for the listener that can later be used to remove it.

You can either listen to a single Slice (by specifying the Index Id and Slice Id as the method's first two parameters), or changes to any Slice (by providing null wildcards).

Both, either, or neither of the indexId and sliceId parameters can be wildcarded with null. You can listen to a specific Slice in a specific Index, any Slice in a specific Index, a specific Slice in any Index, or any Slice in any Index.

The provided listener is a SliceRowIdsListener function, and will be called with a reference to the Indexes object, the Id of the Index, and the Id of the Slice that changed.

Examples

This example creates a Store, an Indexes object, and then registers a listener that responds to any changes to a specific Slice.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const listenerId = indexes.addSliceRowIdsListener(
  'bySpecies',
  'dog',
  (indexes) => {
    console.log('Row Ids for dog slice in bySpecies index changed');
    console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
  },
);

store.setRow('pets', 'toto', {species: 'dog'});
// -> 'Row Ids for dog slice in bySpecies index changed'
// -> ['fido', 'cujo', 'toto']

indexes.delListener(listenerId);

This example creates a Store, an Indexes object, and then registers a listener that responds to any changes to any Slice.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const indexes = createIndexes(store)
  .setIndexDefinition('bySpecies', 'pets', 'species')
  .setIndexDefinition('byColor', 'pets', 'color');

const listenerId = indexes.addSliceRowIdsListener(
  null,
  null,
  (indexes, indexId, sliceId) => {
    console.log(
      `Row Ids for ${sliceId} slice in ${indexId} index changed`,
    );
    console.log(indexes.getSliceRowIds(indexId, sliceId));
  },
);

store.setRow('pets', 'toto', {species: 'dog', color: 'brown'});
// -> 'Row Ids for dog slice in bySpecies index changed'
// -> ['fido', 'cujo', 'toto']
// -> 'Row Ids for brown slice in byColor index changed'
// -> ['fido', 'toto']

indexes.delListener(listenerId);
Since

v1.0.0

addIndexIdsListener

The addIndexIdsListener method registers a listener function with the Indexes object that will be called whenever an Index definition is added or removed.

addIndexIdsListener(listener: IndexIdsListener): string
TypeDescription
listenerIndexIdsListener

The function that will be called whenever an Index definition is added or removed.

returnsstring

The provided listener is an IndexIdsListener function, and will be called with a reference to the Indexes object.

Example

This example creates a Store, an Indexes object, and then registers a listener that responds to the addition and the removal of an Index definition.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
const listenerId = indexes.addIndexIdsListener((indexes) => {
  console.log(indexes.getIndexIds());
});

indexes.setIndexDefinition('bySpecies', 'pets', 'species');
// -> ['bySpecies']
indexes.delIndexDefinition('bySpecies');
// -> []

indexes.delListener(listenerId);
Since

v4.1.0

addSliceIdsListener

The addSliceIdsListener method registers a listener function with the Indexes object that will be called whenever the Slice Ids in an Index change.

addSliceIdsListener(
  indexId: IdOrNull,
  listener: SliceIdsListener,
): string
TypeDescription
indexIdIdOrNull

The Id of the Index to listen to, or null as a wildcard.

listenerSliceIdsListener

The function that will be called whenever the Slice Ids in the Index change.

returnsstring

A unique Id for the listener that can later be used to remove it.

You can either listen to a single Index (by specifying the Index Id as the method's first parameter), or changes to any Index (by providing a null wildcard).

The provided listener is a SliceIdsListener function, and will be called with a reference to the Indexes object, and the Id of the Index that changed.

Examples

This example creates a Store, an Indexes object, and then registers a listener that responds to any changes to a specific Index.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const listenerId = indexes.addSliceIdsListener('bySpecies', (indexes) => {
  console.log('Slice Ids for bySpecies index changed');
  console.log(indexes.getSliceIds('bySpecies'));
});

store.setRow('pets', 'lowly', {species: 'worm'});
// -> 'Slice Ids for bySpecies index changed'
// -> ['dog', 'cat', 'worm']

indexes.delListener(listenerId);

This example creates a Store, an Indexes object, and then registers a listener that responds to any changes to any Index.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'brown'},
});

const indexes = createIndexes(store)
  .setIndexDefinition('bySpecies', 'pets', 'species')
  .setIndexDefinition('byColor', 'pets', 'color');

const listenerId = indexes.addSliceIdsListener(
  null,
  (indexes, indexId) => {
    console.log(`Slice Ids for ${indexId} index changed`);
    console.log(indexes.getSliceIds(indexId));
  },
);

store.setRow('pets', 'lowly', {species: 'worm', color: 'pink'});
// -> 'Slice Ids for bySpecies index changed'
// -> ['dog', 'cat', 'worm']
// -> 'Slice Ids for byColor index changed'
// -> ['brown', 'black', 'pink']

indexes.delListener(listenerId);
Since

v1.0.0

delListener

The delListener method removes a listener that was previously added to the Indexes object.

delListener(listenerId: string): Indexes
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsIndexes

A reference to the Indexes object.

Use the Id returned by whichever method was used to add the listener. Note that the Indexes object may re-use this Id for future listeners added to it.

Example

This example creates a Store, an Indexes object, registers a listener, and then removes it.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const listenerId = indexes.addSliceIdsListener('bySpecies', () => {
  console.log('Slice Ids for bySpecies index changed');
});

store.setRow('pets', 'lowly', {species: 'worm'});
// -> 'Slice Ids for bySpecies index changed'

indexes.delListener(listenerId);

store.setRow('pets', 'toto', {species: 'dog'});
// -> undefined
// The listener is not called.
Since

v1.0.0

Configuration methods

This is the collection of configuration methods within the Indexes interface. There are only two configuration methods, delIndexDefinition and setIndexDefinition.

delIndexDefinition

The delIndexDefinition method removes an existing Index definition.

delIndexDefinition(indexId: string): Indexes
TypeDescription
indexIdstring

The Id of the Index to remove.

returnsIndexes

A reference to the Indexes object.

Example

This example creates a Store, creates an Indexes object, defines a simple Index, and then removes it.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
console.log(indexes.getIndexIds());
// -> ['bySpecies']

indexes.delIndexDefinition('bySpecies');
console.log(indexes.getIndexIds());
// -> []
Since

v1.0.0

setIndexDefinition

The setIndexDefinition method lets you set the definition of an Index.

setIndexDefinition(
  indexId: string,
  tableId: string,
  getSliceIdOrIds?: string | (getCell: GetCell, rowId: string) => string | Ids,
  getSortKey?: string | (getCell: GetCell, rowId: string) => SortKey,
  sliceIdSorter?: (sliceId1: string, sliceId2: string) => number,
  rowIdSorter?: (sortKey1: SortKey, sortKey2: SortKey, sliceId: string) => number,
): Indexes
TypeDescription
indexIdstring

The Id of the Index to define.

tableIdstring

The Id of the Table the Index will be generated from.

getSliceIdOrIds?string | (getCell: GetCell, rowId: string) => string | Ids

Either the Id of the Cell containing, or a function that produces, the Id that is used to indicate which Slice in the Index the Row Id should be in. Defaults to a function that returns '' (meaning that if this getSliceIdOrIds parameter is omitted, the Index will simply contain a single Slice containing all the Row Ids in the Table). Since v2.1, this can return an array of Slice Ids, each of which the Row will then belong to.

getSortKey?string | (getCell: GetCell, rowId: string) => SortKey

Either the Id of the Cell containing, or a function that produces, the value that is used to sort the Row Ids in each Slice.

sliceIdSorter?(sliceId1: string, sliceId2: string) => number

A function that takes two Slice Id values and returns a positive or negative number to indicate how they should be sorted.

rowIdSorter?(sortKey1: SortKey, sortKey2: SortKey, sliceId: string) => number

A function that takes two Row Id values (and a slice Id) and returns a positive or negative number to indicate how they should be sorted.

returnsIndexes

A reference to the Indexes object.

Every Index definition is identified by a unique Id, and if you re-use an existing Id with this method, the previous definition is overwritten.

An Index is a keyed map of Slice objects, each of which is a list of Row Ids from a given Table. Therefore the definition must specify the Table (by its Id) to be indexed.

The Ids in a Slice represent Row objects from a Table that all have a derived string value in common, as described by this method. Those values are used as the key for each Slice in the overall Index object.

Without the third getSliceIdOrIds parameter, the Index will simply have a single Slice, keyed by an empty string. But more often you will specify a Cell value containing the Slice Id that the Row should belong to. Alternatively, a custom function can be provided that produces your own Slice Id from the local Row as a whole. Since v2.1, the custom function can return an array of Slice Ids, each of which the Row will then belong to.

The fourth getSortKey parameter specifies a Cell Id to get a value (or a function that processes a whole Row to get a value) that is used to sort the Row Ids within each Slice in the Index.

The fifth parameter, sliceIdSorter, lets you specify a way to sort the Slice Ids when you access the Index, which may be useful if you are trying to create an alphabetic Index of Row entries. If not specified, the order of the Slice Ids will match the order of Row insertion.

The final parameter, rowIdSorter, lets you specify a way to sort the Row Ids within each Slice, based on the getSortKey parameter. This may be useful if you are trying to keep Rows in a determined order relative to each other in the Index. If omitted, the Row Ids are sorted alphabetically, based on the getSortKey parameter.

The two 'sorter' parameters, sliceIdSorter and rowIdSorter, are functions that take two values and return a positive or negative number for when they are in the wrong or right order, respectively. This is exactly the same as the 'compareFunction' that is used in the standard JavaScript array sort method, with the addition that rowIdSorter also takes the Slice Id parameter, in case you want to sort Row Ids differently in each Slice. You can use the convenient defaultSorter function to default this to be alphanumeric.

Examples

This example creates a Store, creates an Indexes object, and defines a simple Index based on the values in the species Cell.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

console.log(indexes.getSliceIds('bySpecies'));
// -> ['dog', 'cat']
console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
// -> ['fido', 'cujo']

This example creates a Store, creates an Indexes object, and defines an Index based on the first letter of the pets' names.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('byFirst', 'pets', (_, rowId) => rowId[0]);

console.log(indexes.getSliceIds('byFirst'));
// -> ['f', 'c']
console.log(indexes.getSliceRowIds('byFirst', 'f'));
// -> ['fido', 'felix']

This example creates a Store, creates an Indexes object, and defines an Index based on each of the letters present in the pets' names.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  rex: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('containsLetter', 'pets', (_, rowId) =>
  rowId.split(''),
);

console.log(indexes.getSliceIds('containsLetter'));
// -> ['f', 'i', 'd', 'o', 'e', 'l', 'x', 'r']
console.log(indexes.getSliceRowIds('containsLetter', 'i'));
// -> ['fido', 'felix']
console.log(indexes.getSliceRowIds('containsLetter', 'x'));
// -> ['felix', 'rex']

This example creates a Store, creates an Indexes object, and defines an Index based on the first letter of the pets' names. The Slice Ids (and Row Ids within them) are alphabetically sorted.

import {createIndexes, createStore, defaultSorter} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition(
  'byFirst', //              indexId
  'pets', //                 tableId
  (_, rowId) => rowId[0], // each Row's sliceId
  (_, rowId) => rowId, //    each Row's sort key
  defaultSorter, //          sort Slice Ids
  defaultSorter, //          sort Row Ids
);

console.log(indexes.getSliceIds('byFirst'));
// -> ['c', 'f']
console.log(indexes.getSliceRowIds('byFirst', 'f'));
// -> ['felix', 'fido']
Since

v1.0.0

Iterator methods

This is the collection of iterator methods within the Indexes interface. There are only two iterator methods, forEachIndex and forEachSlice.

forEachIndex

The forEachIndex method takes a function that it will then call for each Index in a specified Indexes object.

forEachIndex(indexCallback: IndexCallback): void
TypeDescription
indexCallbackIndexCallback

The function that should be called for every Index.

returnsvoid

This has no return value.

This method is useful for iterating over the structure of the Indexes object in a functional style. The indexCallback parameter is a IndexCallback function that will be called with the Id of each Index, and with a function that can then be used to iterate over each Slice of the Index, should you wish.

Example

This example iterates over each Index in an Indexes object, and lists each Slice Id within them.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const indexes = createIndexes(store)
  .setIndexDefinition('bySpecies', 'pets', 'species')
  .setIndexDefinition('byColor', 'pets', 'color');

indexes.forEachIndex((indexId, forEachSlice) => {
  console.log(indexId);
  forEachSlice((sliceId) => console.log(`- ${sliceId}`));
});
// -> 'bySpecies'
// -> '- dog'
// -> '- cat'
// -> 'byColor'
// -> '- brown'
// -> '- black'
Since

v1.0.0

forEachSlice

The forEachSlice method takes a function that it will then call for each Slice in a specified Index.

forEachSlice(
  indexId: string,
  sliceCallback: SliceCallback,
): void
TypeDescription
indexIdstring

The Id of the Index to iterate over.

sliceCallbackSliceCallback

The function that should be called for every Slice.

returnsvoid

This has no return value.

This method is useful for iterating over the Slice structure of the Index in a functional style. The rowCallback parameter is a RowCallback function that will be called with the Id and value of each Row in the Slice.

Example

This example iterates over each Row in a Slice, and lists its Id.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

indexes.forEachSlice('bySpecies', (sliceId, forEachRow) => {
  console.log(sliceId);
  forEachRow((rowId) => console.log(`- ${rowId}`));
});
// -> 'dog'
// -> '- fido'
// -> '- cujo'
// -> 'cat'
// -> '- felix'
Since

v1.0.0

Lifecycle methods

This is the collection of lifecycle methods within the Indexes interface. There is only one method, destroy.

destroy

The destroy method should be called when this Indexes object is no longer used.

destroy(): void

This guarantees that all of the listeners that the object registered with the underlying Store are removed and it can be correctly garbage collected.

Example

This example creates a Store, adds an Indexes object with a definition (that registers a RowListener with the underlying Store), and then destroys it again, removing the listener.

import {createIndexes, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
console.log(store.getListenerStats().row);
// -> 1

indexes.destroy();

console.log(store.getListenerStats().row);
// -> 0
Since

v1.0.0

Development methods

This is the collection of development methods within the Indexes interface. There is only one method, getListenerStats.

getListenerStats

The getListenerStats method provides a set of statistics about the listeners registered with the Indexes object, and is used for debugging purposes.

getListenerStats(): IndexesListenerStats
returnsIndexesListenerStats

A IndexesListenerStats object containing Indexes listener statistics.

The IndexesListenerStats object contains a breakdown of the different types of listener.

The method is intended to be used during development to ensure your application is not leaking listener registrations, for example.

Example

This example gets the listener statistics of an Indexes object.

import {createIndexes, createStore} from 'tinybase';

const store = createStore();
const indexes = createIndexes(store);
indexes.addSliceIdsListener(null, () => {
  console.log('Slice Ids changed');
});
indexes.addSliceRowIdsListener(null, null, () => {
  console.log('Slice Row Ids changed');
});

console.log(indexes.getListenerStats());
// -> {sliceIds: 1, sliceRowIds: 1}
Since

v1.0.0

Functions

There is one function, createIndexes, within the indexes module.

createIndexes

The createIndexes function creates an Indexes object, and is the main entry point into the indexes module.

createIndexes(store: Store): Indexes
TypeDescription
storeStore

The Store for which to register Index definitions.

returnsIndexes

A reference to the new Indexes object.

A given Store can only have one Indexes object associated with it. If you call this function twice on the same Store, your second call will return a reference to the Indexes object created by the first.

Examples

This example creates an Indexes object.

import {createIndexes, createStore} from 'tinybase';

const store = createStore();
const indexes = createIndexes(store);
console.log(indexes.getIndexIds());
// -> []

This example creates an Indexes object, and calls the method a second time for the same Store to return the same object.

import {createIndexes, createStore} from 'tinybase';

const store = createStore();
const indexes1 = createIndexes(store);
const indexes2 = createIndexes(store);
console.log(indexes1 === indexes2);
// -> true
Since

v1.0.0

Type Aliases

These are the type aliases within the indexes module.

Listener type aliases

This is the collection of listener type aliases within the indexes module. There are only three listener type aliases, SliceRowIdsListener, IndexIdsListener, and SliceIdsListener.

SliceRowIdsListener

The SliceRowIdsListener type describes a function that is used to listen to changes to the Row Ids in a Slice.

(
  indexes: Indexes,
  indexId: Id,
  sliceId: Id,
): void
TypeDescription
indexesIndexes

A reference to the Indexes object that changed.

indexIdId

The Id of the Index that changed.

sliceIdId

The Id of the Slice that changed.

returnsvoid

This has no return value.

A SliceRowIdsListener is provided when using the addSliceRowIdsListener method. See that method for specific examples.

When called, a SliceRowIdsListener is given a reference to the Indexes object, the Id of the Index that changed, and the Id of the Slice whose Row Ids changed.

Since

v1.0.0

IndexIdsListener

The IndexIdsListener type describes a function that is used to listen to Index definitions being added or removed.

(indexes: Indexes): void
TypeDescription
indexesIndexes

A reference to the Indexes object that changed.

returnsvoid

This has no return value.

A IndexIdsListener is provided when using the addIndexIdsListener method. See that method for specific examples.

When called, an IndexIdsListener is given a reference to the Indexes object.

Since

v1.0.0

SliceIdsListener

The SliceIdsListener type describes a function that is used to listen to changes to the Slice Ids in an Index.

(
  indexes: Indexes,
  indexId: Id,
): void
TypeDescription
indexesIndexes

A reference to the Indexes object that changed.

indexIdId

The Id of the Index that changed.

returnsvoid

This has no return value.

A SliceIdsListener is provided when using the addSliceIdsListener method. See that method for specific examples.

When called, a SliceIdsListener is given a reference to the Indexes object, and the Id of the Index that changed.

Since

v1.0.0

Callback type aliases

This is the collection of callback type aliases within the indexes module. There are only two callback type aliases, IndexCallback and SliceCallback.

IndexCallback

The IndexCallback type describes a function that takes an Index's Id and a callback to loop over each Slice within it.

(
  indexId: Id,
  forEachSlice: (sliceCallback: SliceCallback) => void,
): void
TypeDescription
indexIdId

The Id of the Index that the callback can operate on.

forEachSlice(sliceCallback: SliceCallback) => void
returnsvoid

This has no return value.

A IndexCallback is provided when using the forEachIndex method, so that you can do something based on every Index in the Indexes object. See that method for specific examples.

Since

v1.0.0

SliceCallback

The SliceCallback type describes a function that takes a Slice's Id and a callback to loop over each Row within it.

(
  sliceId: Id,
  forEachRow: (rowCallback: RowCallback) => void,
): void
TypeDescription
sliceIdId

The Id of the Slice that the callback can operate on.

forEachRow(rowCallback: RowCallback) => void

A function that will let you iterate over the Row objects in this Slice.

returnsvoid

This has no return value.

A SliceCallback is provided when using the forEachSlice method, so that you can do something based on every Slice in an Index. See that method for specific examples.

Since

v1.0.0

Concept type aliases

This is the collection of concept type aliases within the indexes module. There are only two concept type aliases, Index and Slice.

Index

The Index type represents the concept of a map of Slice objects, keyed by Id.

{[sliceId: Id]: Slice}

The Ids in a Slice represent Row objects from a Table that all have a derived string value in common, as described by the setIndexDefinition method. Those values are used as the key for each Slice in the overall Index object.

Note that the Index type is not actually used in the API, and you instead enumerate and access its structure with the getSliceIds method and getSliceRowIds method.

Since

v1.0.0

Slice

The Slice type represents the concept of a set of Row objects that comprise part of an Index.

Ids

The Ids in a Slice represent Row objects from a Table that all have a derived string value in common, as described by the setIndexDefinition method.

Note that the Slice type is not actually used in the API, and you instead get Row Ids directly with the getSliceRowIds method.

Since

v1.0.0

Development type aliases

This is the collection of development type aliases within the indexes module. There is only one type alias, IndexesListenerStats.

IndexesListenerStats

The IndexesListenerStats type describes the number of listeners registered with the Indexes object, and can be used for debugging purposes.

{
  sliceIds: number;
  sliceRowIds: number;
}
TypeDescription
sliceIdsnumber

The number of SlideIdsListener functions registered with the Indexes object.

sliceRowIdsnumber

The number of SliceRowIdsListener functions registered with the Indexes object.

A IndexesListenerStats object is returned from the getListenerStats method.

Since

v1.0.0

relationships

The relationships module of the TinyBase project provides the ability to create and track relationships between the data in Store objects.

The main entry point to this module is the createRelationships function, which returns a new Relationships object. From there, you can create new Relationship definitions, access the associations within those Relationships directly, and register listeners for when they change.

Since

v1.0.0

Interfaces

There is one interface, Relationships, within the relationships module.

Relationships

A Relationships object lets you associate a Row in a one Table with the Id of a Row in another Table.

This is useful for creating parent-child relationships between the data in different Table objects, but it can also be used to model a linked list of Row objects in the same Table.

Create a Relationships object easily with the createRelationships function. From there, you can add new Relationship definitions (with the setRelationshipDefinition method), query their contents (with the getRemoteRowId method, the getLocalRowIds method, and the getLinkedRowIds method), and add listeners for when they change (with the addRemoteRowIdListener method, the addLocalRowIdsListener method, and the addLinkedRowIdsListener method).

This module defaults to creating relationships between Row objects by using one of their Cell values. However, far more complex relationships can be configured with a custom function.

Example

This example shows a very simple lifecycle of a Relationships object: from creation, to adding definitions (both local/remote table and linked list), getting their contents, and then registering and removing listeners for them.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', next: 'felix'},
    felix: {species: 'cat', next: 'cujo'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);

// A local/remote table relationship:
relationships.setRelationshipDefinition(
  'petSpecies', //  relationshipId
  'pets', //        localTableId to link from
  'species', //     remoteTableId to link to
  'species', //     cellId containing remote key
);
console.log(relationships.getRemoteRowId('petSpecies', 'fido'));
// -> 'dog'
console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
// -> ['fido', 'cujo']

// A linked list relationship:
relationships.setRelationshipDefinition(
  'petSequence', // relationshipId
  'pets', //        localTableId to link from
  'pets', //        the same remoteTableId to link within
  'next', //        cellId containing link key
);
console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
// -> ['fido', 'felix', 'cujo']

const listenerId1 = relationships.addLocalRowIdsListener(
  'petSpecies',
  'dog',
  () => {
    console.log('petSpecies relationship (to dog) changed');
    console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
  },
);
const listenerId2 = relationships.addLinkedRowIdsListener(
  'petSequence',
  'fido',
  () => {
    console.log('petSequence linked list (from fido) changed');
    console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
  },
);

store.setRow('pets', 'toto', {species: 'dog'});
// -> 'petSpecies relationship (to dog) changed'
// -> ['fido', 'cujo', 'toto']

store.setCell('pets', 'cujo', 'next', 'toto');
// -> 'petSequence linked list (from fido) changed'
// -> ['fido', 'felix', 'cujo', 'toto']

relationships.delListener(listenerId1);
relationships.delListener(listenerId2);
relationships.destroy();
See also
Since

v1.0.0

Getter methods

This is the collection of getter methods within the Relationships interface. There are 8 getter methods in total.

getStore

The getStore method returns a reference to the underlying Store that is backing this Relationships object.

getStore(): Store
returnsStore

A reference to the Store.

Example

This example creates a Relationships object against a newly-created Store and then gets its reference in order to update its data.

import {createRelationships, createStore} from 'tinybase';
const relationships = createRelationships(createStore());
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);
relationships.getStore().setCell('pets', 'fido', 'species', 'dog');
console.log(relationships.getRemoteRowId('petSpecies', 'fido'));
// -> 'dog'
Since

v1.0.0

getLocalTableId

The getLocalTableId method returns the Id of the underlying local Table that is used in the Relationship.

getLocalTableId(relationshipId: string): undefined | string
TypeDescription
relationshipIdstring

The Id of a Relationship.

returnsundefined | string

The Id of the local Table backing the Relationship, or undefined.

If the Relationship Id is invalid, the method returns undefined.

Example

This example creates a Relationship object, a single Relationship definition, and then queries it (and a non-existent definition) to get the underlying local Table Id.

import {createRelationships, createStore} from 'tinybase';
const relationships = createRelationships(createStore());
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

console.log(relationships.getLocalTableId('petSpecies'));
// -> 'pets'
console.log(relationships.getLocalTableId('petColor'));
// -> undefined
Since

v1.0.0

getRemoteTableId

The getRemoteTableId method returns the Id of the underlying remote Table that is used in the Relationship.

getRemoteTableId(relationshipId: string): undefined | string
TypeDescription
relationshipIdstring

The Id of a Relationship.

returnsundefined | string

The Id of the remote Table backing the Relationship, or undefined.

If the Relationship Id is invalid, the method returns undefined.

Example

This example creates a Relationship object, a single Relationship definition, and then queries it (and a non-existent definition) to get the underlying remote Table Id.

import {createRelationships, createStore} from 'tinybase';
const relationships = createRelationships(createStore());
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

console.log(relationships.getRemoteTableId('petSpecies'));
// -> 'species'
console.log(relationships.getRemoteTableId('petColor'));
// -> undefined
Since

v1.0.0

getLinkedRowIds

The getLinkedRowIds method gets the linked Row Ids for a given Row in a linked list Relationship.

getLinkedRowIds(
  relationshipId: string,
  firstRowId: string,
): Ids
TypeDescription
relationshipIdstring

The Id of the Relationship.

firstRowIdstring

The Id of the first Row in the linked list Relationship.

returnsIds

The linked Row Ids in the Relationship.

A linked list Relationship is one that has the same Table specified as both local Table Id and remote Table Id, allowing you to create a sequence of Row objects within that one Table.

If the identified Relationship or Row does not exist (or if the definition references a Table that does not exist) then an array containing just the first Row Id is returned.

Example

This example creates a Store, creates a Relationships object, and defines a simple linked list Relationship. It then uses getLinkedRowIds to see the linked Row Ids in the Relationship (and also the linked Row Ids for a Row that does not exist, and for a Relationship that has not been defined).

import {createRelationships, createStore} from 'tinybase';
const store = createStore().setTable('pets', {
  fido: {species: 'dog', next: 'felix'},
  felix: {species: 'cat', next: 'cujo'},
  cujo: {species: 'dog'},
});

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSequence',
  'pets',
  'pets',
  'next',
);

console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
// -> ['fido', 'felix', 'cujo']
console.log(relationships.getLinkedRowIds('petSequence', 'felix'));
// -> ['felix', 'cujo']
console.log(relationships.getLinkedRowIds('petSequence', 'toto'));
// -> ['toto']
console.log(relationships.getLinkedRowIds('petFriendships', 'fido'));
// -> ['fido']
Since

v1.0.0

getLocalRowIds

The getLocalRowIds method gets the local Row Ids for a given remote Row in a Relationship.

getLocalRowIds(
  relationshipId: string,
  remoteRowId: string,
): Ids
TypeDescription
relationshipIdstring

The Id of the Relationship.

remoteRowIdstring

The Id of the remote Row in the Relationship.

returnsIds

The local Row Ids in the Relationship, or an empty array.

If the identified Relationship or Row does not exist (or if the definition references a Table that does not exist) then an empty array is returned.

Example

This example creates a Store, creates a Relationships object, and defines a simple Relationship. It then uses getLocalRowIds to see the local Row Ids in the Relationship (and also the local Row Ids for a remote Row that does not exist, and for a Relationship that has not been defined).

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
// -> ['fido', 'cujo']
console.log(relationships.getLocalRowIds('petSpecies', 'worm'));
// -> []
console.log(relationships.getLocalRowIds('petColor', 'brown'));
// -> []
Since

v1.0.0

getRemoteRowId

The getRemoteRowId method gets the remote Row Id for a given local Row in a Relationship.

getRemoteRowId(
  relationshipId: string,
  localRowId: string,
): undefined | string
TypeDescription
relationshipIdstring

The Id of the Relationship.

localRowIdstring

The Id of the local Row in the Relationship.

returnsundefined | string

The remote Row Id in the Relationship, or undefined.

If the identified Relationship or Row does not exist (or if the definition references a Table that does not exist) then undefined is returned.

Example

This example creates a Store, creates a Relationships object, and defines a simple Relationship. It then uses getRemoteRowId to see the remote Row Id in the Relationship (and also the remote Row Ids for a local Row that does not exist, and for a Relationship that has not been defined).

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

console.log(relationships.getRemoteRowId('petSpecies', 'fido'));
// -> 'dog'
console.log(relationships.getRemoteRowId('petSpecies', 'toto'));
// -> undefined
console.log(relationships.getRemoteRowId('petColor', 'fido'));
// -> undefined
Since

v1.0.0

getRelationshipIds

The getRelationshipIds method returns an array of the Relationship Ids registered with this Relationships object.

getRelationshipIds(): Ids
returnsIds

An array of Ids.

Example

This example creates a Relationships object with two definitions, and then gets the Ids of the definitions.

import {createRelationships, createStore} from 'tinybase';
const relationships = createRelationships(createStore())
  .setRelationshipDefinition('petSpecies', 'pets', 'species', 'species')
  .setRelationshipDefinition('petSequence', 'pets', 'pets', 'next');
console.log(relationships.getRelationshipIds());
// -> ['petSpecies', 'petSequence']
Since

v1.0.0

hasRelationship

The hasRelationship method returns a boolean indicating whether a given Relationship exists in the Relationships object.

hasRelationship(relationshipId: string): boolean
TypeDescription
relationshipIdstring

The Id of a possible Relationship in the Relationships object.

returnsboolean

Whether a Relationship with that Id exists.

Example

This example shows two simple Relationship existence checks.

import {createRelationships, createStore} from 'tinybase';
const relationships = createRelationships(
  createStore(),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');
console.log(relationships.hasRelationship('petSpecies'));
// -> true
console.log(relationships.hasRelationship('petColor'));
// -> false
Since

v1.0.0

Listener methods

This is the collection of listener methods within the Relationships interface. There are 5 listener methods in total.

addLinkedRowIdsListener

The addLinkedRowIdsListener method registers a listener function with the Relationships object that will be called whenever the linked Row Ids in a linked list Relationship change.

addLinkedRowIdsListener(
  relationshipId: string,
  firstRowId: string,
  listener: LinkedRowIdsListener,
): string
TypeDescription
relationshipIdstring

The Id of the Relationship to listen to.

firstRowIdstring

The Id of the first Row of the linked list to listen to.

listenerLinkedRowIdsListener

The function that will be called whenever the linked Row Ids change.

returnsstring

A unique Id for the listener that can later be used to remove it.

A linked list Relationship is one that has the same Table specified as both local Table Id and remote Table Id, allowing you to create a sequence of Row objects within that one Table.

You listen to changes to a linked list starting from a single first Row by specifying the Relationship Id and local Row Id as the method's first two parameters.

Unlike other listener registration methods, you cannot provide null wildcards for the first two parameters of the addLinkedRowIdsListener method. This prevents the prohibitive expense of tracking all the possible linked lists (and partial linked lists within them) in a Store.

The provided listener is a LinkedRowIdsListener function, and will be called with a reference to the Relationships object, the Id of the Relationship, and the Id of the first Row that had its linked list change.

Example

This example creates a Store, a Relationships object, and then registers a listener that responds to any changes to a specific first Row's linked Row objects.

import {createRelationships, createStore} from 'tinybase';
const store = createStore().setTable('pets', {
  fido: {species: 'dog', next: 'felix'},
  felix: {species: 'cat', next: 'cujo'},
  cujo: {species: 'dog'},
});

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSequence',
  'pets',
  'pets',
  'next',
);

const listenerId = relationships.addLinkedRowIdsListener(
  'petSequence',
  'fido',
  (relationships) => {
    console.log('petSequence linked list (from fido) changed');
    console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
  },
);

store.setRow('pets', 'toto', {species: 'dog'});
store.setCell('pets', 'cujo', 'next', 'toto');
// -> 'petSequence linked list (from fido) changed'
// -> ['fido', 'felix', 'cujo', 'toto']

relationships.delListener(listenerId);
Since

v1.0.0

addLocalRowIdsListener

The addLocalRowIdsListener method registers a listener function with the Relationships object that will be called whenever the local Row Ids in a Relationship change.

addLocalRowIdsListener(
  relationshipId: IdOrNull,
  remoteRowId: IdOrNull,
  listener: LocalRowIdsListener,
): string
TypeDescription
relationshipIdIdOrNull

The Id of the Relationship to listen to, or null as a wildcard.

remoteRowIdIdOrNull

The Id of the remote Row to listen to, or null as a wildcard.

listenerLocalRowIdsListener

The function that will be called whenever the local Row Ids change.

returnsstring

A unique Id for the listener that can later be used to remove it.

You can either listen to a single local Row (by specifying the Relationship Id and local Row Id as the method's first two parameters), or changes to any local Row (by providing a null wildcards).

Both, either, or neither of the relationshipId and remoteRowId parameters can be wildcarded with null. You can listen to a specific remote Row in a specific Relationship, any remote Row in a specific Relationship, a specific remote Row in any Relationship, or any remote Row in any Relationship.

The provided listener is a LocalRowIdsListener function, and will be called with a reference to the Relationships object, the Id of the Relationship, and the Id of the remote Row that had its local Row objects change.

Examples

This example creates a Store, a Relationships object, and then registers a listener that responds to any changes to a specific remote Row's local Row objects.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    wolf: {price: 10},
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

const listenerId = relationships.addLocalRowIdsListener(
  'petSpecies',
  'dog',
  (relationships) => {
    console.log('petSpecies relationship (to dog) changed');
    console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
  },
);

store.setRow('pets', 'toto', {species: 'dog'});
// -> 'petSpecies relationship (to dog) changed'
// -> ['fido', 'cujo', 'toto']

relationships.delListener(listenerId);

This example creates a Store, a Relationships object, and then registers a listener that responds to any changes to any remote Row's local Row objects.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'brown'},
    toto: {species: 'dog', color: 'grey'},
  })
  .setTable('species', {
    wolf: {price: 10},
    dog: {price: 5},
    cat: {price: 4},
  })
  .setTable('color', {
    brown: {discount: 0.1},
    black: {discount: 0},
    grey: {discount: 0.2},
  });

const relationships = createRelationships(store)
  .setRelationshipDefinition('petSpecies', 'pets', 'species', 'species')
  .setRelationshipDefinition('petColor', 'pets', 'color', 'color');

const listenerId = relationships.addLocalRowIdsListener(
  null,
  null,
  (relationships, relationshipId, remoteRowId) => {
    console.log(
      `${relationshipId} relationship (to ${remoteRowId}) changed`,
    );
    console.log(relationships.getLocalRowIds(relationshipId, remoteRowId));
  },
);

store.setRow('pets', 'cujo', {species: 'wolf', color: 'grey'});
// -> 'petSpecies relationship (to dog) changed'
// -> ['fido', 'toto']
// -> 'petSpecies relationship (to wolf) changed'
// -> ['cujo']
// -> 'petColor relationship (to brown) changed'
// -> ['fido']
// -> 'petColor relationship (to grey) changed'
// -> ['toto', 'cujo']

relationships.delListener(listenerId);
Since

v1.0.0

addRemoteRowIdListener

The addRemoteRowIdListener method registers a listener function with the Relationships object that will be called whenever a remote Row Id in a Relationship changes.

addRemoteRowIdListener(
  relationshipId: IdOrNull,
  localRowId: IdOrNull,
  listener: RemoteRowIdListener,
): string
TypeDescription
relationshipIdIdOrNull

The Id of the Relationship to listen to, or null as a wildcard.

localRowIdIdOrNull

The Id of the local Row to listen to, or null as a wildcard.

listenerRemoteRowIdListener

The function that will be called whenever the remote Row Id changes.

returnsstring

A unique Id for the listener that can later be used to remove it.

You can either listen to a single local Row (by specifying the Relationship Id and local Row Id as the method's first two parameters), or changes to any local Row (by providing a null wildcards).

Both, either, or neither of the relationshipId and localRowId parameters can be wildcarded with null. You can listen to a specific local Row in a specific Relationship, any local Row in a specific Relationship, a specific local Row in any Relationship, or any local Row in any Relationship.

The provided listener is a RemoteRowIdListener function, and will be called with a reference to the Relationships object, the Id of the Relationship, and the Id of the local Row that had its remote Row change.

Examples

This example creates a Store, a Relationships object, and then registers a listener that responds to any changes to a specific local Row's remote Row.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    wolf: {price: 10},
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

const listenerId = relationships.addRemoteRowIdListener(
  'petSpecies',
  'cujo',
  (relationships) => {
    console.log('petSpecies relationship (from cujo) changed');
    console.log(relationships.getRemoteRowId('petSpecies', 'cujo'));
  },
);

store.setCell('pets', 'cujo', 'species', 'wolf');
// -> 'petSpecies relationship (from cujo) changed'
// -> 'wolf'

relationships.delListener(listenerId);

This example creates a Store, a Relationships object, and then registers a listener that responds to any changes to any local Row's remote Row. It also illustrates how you can use the getStore method and the getRemoteRowId method to resolve the remote Row as a whole.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'brown'},
  })
  .setTable('species', {
    wolf: {price: 10},
    dog: {price: 5},
    cat: {price: 4},
  })
  .setTable('color', {
    brown: {discount: 0.1},
    black: {discount: 0},
    grey: {discount: 0.2},
  });

const relationships = createRelationships(store)
  .setRelationshipDefinition('petSpecies', 'pets', 'species', 'species')
  .setRelationshipDefinition('petColor', 'pets', 'color', 'color');

const listenerId = relationships.addRemoteRowIdListener(
  null,
  null,
  (relationships, relationshipId, localRowId) => {
    console.log(
      `${relationshipId} relationship (from ${localRowId}) changed`,
    );
    console.log(relationships.getRemoteRowId(relationshipId, localRowId));
    console.log(
      relationships
        .getStore()
        .getRow(
          relationships.getRemoteTableId(relationshipId),
          relationships.getRemoteRowId(relationshipId, localRowId),
        ),
    );
  },
);

store.setRow('pets', 'cujo', {species: 'wolf', color: 'grey'});
// -> 'petSpecies relationship (from cujo) changed'
// -> 'wolf'
// -> {price: 10}
// -> 'petColor relationship (from cujo) changed'
// -> 'grey'
// -> {discount: 0.2}

relationships.delListener(listenerId);
Since

v1.0.0

addRelationshipIdsListener

The addRelationshipIdsListener method registers a listener function with the Relationships object that will be called whenever a Relationship definition is added or removed.

addRelationshipIdsListener(listener: RelationshipIdsListener): string
TypeDescription
listenerRelationshipIdsListener

The function that will be called whenever a Relationship definition is added or removed.

returnsstring

The provided listener is a RelationshipIdsListener function, and will be called with a reference to the Relationships object.

Example

This example creates a Store, a Relationships object, and then registers a listener that responds to the addition and the removal of a Relationship definition.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    wolf: {price: 10},
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
const listenerId = relationships.addRelationshipIdsListener(
  (relationships) => {
    console.log(relationships.getRelationshipIds());
  },
);

relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);
// -> ['petSpecies']
relationships.delRelationshipDefinition('petSpecies');
// -> []

relationships.delListener(listenerId);
Since

v4.1.0

delListener

The delListener method removes a listener that was previously added to the Relationships object.

delListener(listenerId: string): Relationships
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsRelationships

A reference to the Relationships object.

Use the Id returned by whichever method was used to add the listener. Note that the Relationships object may re-use this Id for future listeners added to it.

Example

This example creates a Store, a Relationships object, registers a listener, and then removes it.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    wolf: {price: 10},
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

const listenerId = relationships.addLocalRowIdsListener(
  'petSpecies',
  'dog',
  () => {
    console.log('petSpecies relationship (to dog) changed');
  },
);

store.setRow('pets', 'toto', {species: 'dog'});
// -> 'petSpecies relationship (to dog) changed'

relationships.delListener(listenerId);

store.setRow('pets', 'toto', {species: 'dog'});
// -> undefined
// The listener is not called.
Since

v1.0.0

Configuration methods

This is the collection of configuration methods within the Relationships interface. There are only two configuration methods, delRelationshipDefinition and setRelationshipDefinition.

delRelationshipDefinition

The delRelationshipDefinition method removes an existing Relationship definition.

delRelationshipDefinition(relationshipId: string): Relationships
TypeDescription
relationshipIdstring

The Id of the Relationship to remove.

returnsRelationships

A reference to the Relationships object.

Example

This example creates a Store, creates a Relationships object, defines a simple Relationship, and then removes it.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);
console.log(relationships.getRelationshipIds());
// -> ['petSpecies']

relationships.delRelationshipDefinition('petSpecies');
console.log(relationships.getRelationshipIds());
// -> []
Since

v1.0.0

setRelationshipDefinition

The setRelationshipDefinition method lets you set the definition of a Relationship.

setRelationshipDefinition(
  relationshipId: string,
  localTableId: string,
  remoteTableId: string,
  getRemoteRowId: string | (getCell: GetCell, localRowId: string) => string,
): Relationships
TypeDescription
relationshipIdstring

The Id of the Relationship to define.

localTableIdstring

The Id of the local Table for the Relationship.

remoteTableIdstring

The Id of the remote Table for the Relationship (or the same as the localTableId in the case of a linked list).

getRemoteRowIdstring | (getCell: GetCell, localRowId: string) => string

Either the Id of the Cell containing, or a function that produces, the Id that is used to indicate which Row in the remote Table a local Row is related to.

returnsRelationships

A reference to the Relationships object.

Every Relationship definition is identified by a unique Id, and if you re-use an existing Id with this method, the previous definition is overwritten.

An Relationship is based on connections between Row objects, often in two different Table objects. Therefore the definition requires the localTableId parameter to specify the 'local' Table to create the Relationship from, and the remoteTableId parameter to specify the 'remote' Table to create Relationship to.

A linked list Relationship is one that has the same Table specified as both local Table Id and remote Table Id, allowing you to create a sequence of Row objects within that one Table.

A local Row is related to a remote Row by specifying which of its (local) Cell values contains the (remote) Row Id, using the getRemoteRowId parameter. Alternatively, a custom function can be provided that produces your own remote Row Id from the local Row as a whole.

Examples

This example creates a Store, creates a Relationships object, and defines a simple Relationship based on the values in the species Cell of the pets Table that relates a Row to another in the species Table.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies', // relationshipId
  'pets', //       localTableId to link from
  'species', //    remoteTableId to link to
  'species', //    cellId containing remote key
);

console.log(relationships.getRemoteRowId('petSpecies', 'fido'));
// -> 'dog'
console.log(relationships.getLocalRowIds('petSpecies', 'dog'));
// -> ['fido', 'cujo']

This example creates a Store, creates a Relationships object, and defines a linked list Relationship based on the values in the next Cell of the pets Table that relates a Row to another in the same Table.

import {createRelationships, createStore} from 'tinybase';
const store = createStore().setTable('pets', {
  fido: {species: 'dog', next: 'felix'},
  felix: {species: 'cat', next: 'cujo'},
  cujo: {species: 'dog'},
});

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSequence', // relationshipId
  'pets', //        localTableId to link from
  'pets', //        the same remoteTableId to link within
  'next', //        cellId containing link key
);

console.log(relationships.getLinkedRowIds('petSequence', 'fido'));
// -> ['fido', 'felix', 'cujo']
Since

v1.0.0

Iterator methods

This is the collection of iterator methods within the Relationships interface. There is only one method, forEachRelationship.

forEachRelationship

The forEachRelationship method takes a function that it will then call for each Relationship in a specified Relationships object.

forEachRelationship(relationshipCallback: RelationshipCallback): void
TypeDescription
relationshipCallbackRelationshipCallback

The function that should be called for every Relationship.

returnsvoid

This has no return value.

This method is useful for iterating over the structure of the Relationships object in a functional style. The relationshipCallback parameter is a RelationshipCallback function that will be called with the Id of each Relationship, and with a function that can then be used to iterate over each local Row involved in the Relationship.

Example

This example iterates over each Relationship in a Relationships object, and lists each Row Id within them.

import {createRelationships, createStore} from 'tinybase';
const store = createStore().setTable('pets', {
  fido: {species: 'dog', next: 'felix'},
  felix: {species: 'cat', next: 'cujo'},
  cujo: {species: 'dog'},
});
const relationships = createRelationships(store)
  .setRelationshipDefinition('petSpecies', 'pets', 'species', 'species')
  .setRelationshipDefinition('petSequence', 'pets', 'pets', 'next');

relationships.forEachRelationship((relationshipId, forEachRow) => {
  console.log(relationshipId);
  forEachRow((rowId) => console.log(`- ${rowId}`));
});
// -> 'petSpecies'
// -> '- fido'
// -> '- felix'
// -> '- cujo'
// -> 'petSequence'
// -> '- fido'
// -> '- felix'
// -> '- cujo'
Since

v1.0.0

Lifecycle methods

This is the collection of lifecycle methods within the Relationships interface. There is only one method, destroy.

destroy

The destroy method should be called when this Relationships object is no longer used.

destroy(): void

This guarantees that all of the listeners that the object registered with the underlying Store are removed and it can be correctly garbage collected.

Example

This example creates a Store, adds a Relationships object with a definition (that registers a RowListener with the underlying Store), and then destroys it again, removing the listener.

import {createRelationships, createStore} from 'tinybase';
const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  })
  .setTable('species', {
    wolf: {price: 10},
    dog: {price: 5},
    cat: {price: 4},
  });

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);
console.log(store.getListenerStats().row);
// -> 1

relationships.destroy();

console.log(store.getListenerStats().row);
// -> 0
Since

v1.0.0

Development methods

This is the collection of development methods within the Relationships interface. There is only one method, getListenerStats.

getListenerStats

The getListenerStats method provides a set of statistics about the listeners registered with the Relationships object, and is used for debugging purposes.

getListenerStats(): RelationshipsListenerStats
returnsRelationshipsListenerStats

A RelationshipsListenerStats object containing Relationships listener statistics.

The RelationshipsListenerStats object contains a breakdown of the different types of listener.

The method is intended to be used during development to ensure your application is not leaking listener registrations, for example.

Example

This example gets the listener statistics of a Relationships object.

import {createRelationships, createStore} from 'tinybase';
const store = createStore();
const relationships = createRelationships(store);
relationships.addRemoteRowIdListener(null, null, () => {
  console.log('Remote Row Id changed');
});
relationships.addLocalRowIdsListener(null, null, () => {
  console.log('Local Row Id changed');
});

const listenerStats = relationships.getListenerStats();
console.log(listenerStats.remoteRowId);
// -> 1
console.log(listenerStats.localRowIds);
// -> 1
Since

v1.0.0

Functions

There is one function, createRelationships, within the relationships module.

createRelationships

The createRelationships function creates a Relationships object, and is the main entry point into the relationships module.

createRelationships(store: Store): Relationships
TypeDescription
storeStore

The Store for which to register Relationships.

returnsRelationships

A reference to the new Relationships object.

A given Store can only have one Relationships object associated with it. If you call this function twice on the same Store, your second call will return a reference to the Relationships object created by the first.

Examples

This example creates a Relationships object.

import {createRelationships, createStore} from 'tinybase';
const store = createStore();
const relationships = createRelationships(store);
console.log(relationships.getRelationshipIds());
// -> []

This example creates a Relationships object, and calls the method a second time for the same Store to return the same object.

import {createRelationships, createStore} from 'tinybase';
const store = createStore();
const relationships1 = createRelationships(store);
const relationships2 = createRelationships(store);
console.log(relationships1 === relationships2);
// -> true
Since

v1.0.0

Type Aliases

These are the type aliases within the relationships module.

Listener type aliases

This is the collection of listener type aliases within the relationships module. There are 4 listener type aliases in total.

LinkedRowIdsListener

The LinkedRowIdsListener type describes a function that is used to listen to changes to the local Row Id ends of a Relationship.

(
  relationships: Relationships,
  relationshipId: Id,
  firstRowId: Id,
): void
TypeDescription
relationshipsRelationships

A reference to the Relationships object that changed.

relationshipIdId

The Id of the Relationship that changed.

firstRowIdId

The Id of the first Row of the the linked list whose members changed.

returnsvoid

This has no return value.

A LinkedRowIdsListener is provided when using the addLinkedRowIdsListener method. See that method for specific examples.

When called, a LinkedRowIdsListener is given a reference to the Relationships object, the Id of the Relationship that changed, and the Id of the first Row of the the linked list whose members changed.

Since

v1.0.0

LocalRowIdsListener

The LocalRowIdsListener type describes a function that is used to listen to changes to the local Row Id ends of a Relationship.

(
  relationships: Relationships,
  relationshipId: Id,
  remoteRowId: Id,
): void
TypeDescription
relationshipsRelationships

A reference to the Relationships object that changed.

relationshipIdId

The Id of the Relationship that changed.

remoteRowIdId

The Id of the remote Row whose local Row Ids changed.

returnsvoid

This has no return value.

A LocalRowIdsListener is provided when using the addLocalRowIdsListener method. See that method for specific examples.

When called, a LocalRowIdsListener is given a reference to the Relationships object, the Id of the Relationship that changed, and the Id of the remote Row whose local Row Ids changed.

Since

v1.0.0

RemoteRowIdListener

The RemoteRowIdListener type describes a function that is used to listen to changes to the remote Row Id end of a Relationship.

(
  relationships: Relationships,
  relationshipId: Id,
  localRowId: Id,
): void
TypeDescription
relationshipsRelationships

A reference to the Relationships object that changed.

relationshipIdId

The Id of the Relationship that changed.

localRowIdId

The Id of the local Row whose remote Row Id changed.

returnsvoid

This has no return value.

A RemoteRowIdListener is provided when using the addRemoteRowIdListener method. See that method for specific examples.

When called, a RemoteRowIdListener is given a reference to the Relationships object, the Id of the Relationship that changed, and the Id of the local Row whose remote Row Id changed.

Since

v1.0.0

RelationshipIdsListener

The RelationshipIdsListener type describes a function that is used to listen to Relationship definitions being added or removed.

(relationships: Relationships): void
TypeDescription
relationshipsRelationships

A reference to the Relationships object that changed.

returnsvoid

This has no return value.

A RelationshipIdsListener is provided when using the addRelationshipIdsListener method. See that method for specific examples.

When called, a RelationshipIdsListener is given a reference to the Relationships object.

Since

v1.0.0

Callback type aliases

This is the collection of callback type aliases within the relationships module. There is only one type alias, RelationshipCallback.

RelationshipCallback

The RelationshipCallback type describes a function that takes a Relationship's Id and a callback to loop over each local Row within it.

(
  relationshipId: Id,
  forEachRow: (rowCallback: RowCallback) => void,
): void
TypeDescription
relationshipIdId

The Id of the Relationship that the callback can operate on.

forEachRow(rowCallback: RowCallback) => void

A function that will let you iterate over the local Row objects in this Relationship.

returnsvoid

This has no return value.

A RelationshipCallback is provided when using the forEachRelationship method, so that you can do something based on every Relationship in the Relationships object. See that method for specific examples.

Since

v1.0.0

Concept type aliases

This is the collection of concept type aliases within the relationships module. There is only one type alias, Relationship.

Relationship

The Relationship type represents the concept of a map that connects one Row object to another, often in another Table.

{
  remoteRowId: {[localRowId: Id]: Id};
  localRowIds: {[remoteRowId: Id]: Ids};
  linkedRowIds: {[firstRowId: Id]: Ids};
}
TypeDescription
remoteRowId{[localRowId: Id]: Id}
localRowIds{[remoteRowId: Id]: Ids}
linkedRowIds{[firstRowId: Id]: Ids}

The Relationship has a one-to-many nature. One local Row Id is linked to one remote Row Id (in the remote Table), as described by the setRelationshipDefinition method - and one remote Row Id may map back to multiple local Row Ids (in the local Table).

A Relationship where the local Table is the same as the remote Table can be used to model a 'linked list', where Row A references Row B, Row B references Row C, and so on.

Note that the Relationship type is not actually used in the API, and you instead enumerate and access its structure with the getRemoteRowId method, the getLocalRowIds method, and the getLinkedRowIds method.

Since

v1.0.0

Development type aliases

This is the collection of development type aliases within the relationships module. There is only one type alias, RelationshipsListenerStats.

RelationshipsListenerStats

The RelationshipsListenerStats type describes the number of listeners registered with the Relationships object, and can be used for debugging purposes.

{
  remoteRowId: number;
  localRowIds: number;
  linkedRowIds: number;
}
TypeDescription
remoteRowIdnumber

The number of RemoteRowIdListener functions registered with the Relationships object.

localRowIdsnumber

The number of LocalRowIdsListener functions registered with the Relationships object.

linkedRowIdsnumber

The number of LinkedRowId functions registered with the Relationships object.

A RelationshipsListenerStats object is returned from the getListenerStats method.

Since

v1.0.0

queries

The queries module of the TinyBase project provides the ability to create and track queries of the data in Store objects.

The main entry point to using the queries module is the createQueries function, which returns a new Queries object. That object in turn has methods that let you create new query definitions, access their results directly, and register listeners for when those results change.

Since

v2.0.0

Interfaces

There is one interface, Queries, within the queries module.

Queries

A Queries object lets you create and track queries of the data in Store objects.

This is useful for creating a reactive view of data that is stored in physical tables: selecting columns, joining tables together, filtering rows, aggregating data, sorting it, and so on.

This provides a generalized query concept for Store data. If you just want to create and track metrics, indexes, or relationships between rows, you may prefer to use the dedicated Metrics, Indexes, and Relationships objects, which have simpler APIs.

Create a Queries object easily with the createQueries function. From there, you can add new query definitions (with the setQueryDefinition method), query the results (with the getResultTable method, the getResultRow method, the getResultCell method, and so on), and add listeners for when they change (with the addResultTableListener method, the addResultRowListener method, the addResultCellListener method, and so on).

Example

This example shows a very simple lifecycle of a Queries object: from creation, to adding definitions, getting their contents, and then registering and removing listeners for them.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', color: 'brown', ownerId: '1'},
    felix: {species: 'cat', color: 'black', ownerId: '2'},
    cujo: {species: 'dog', color: 'black', ownerId: '3'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
    worm: {price: 1},
  })
  .setTable('owners', {
    '1': {name: 'Alice'},
    '2': {name: 'Bob'},
    '3': {name: 'Carol'},
  });

const queries = createQueries(store);

// A filtered table query:
queries.setQueryDefinition('blackPets', 'pets', ({select, where}) => {
  select('species');
  where('color', 'black');
});
console.log(queries.getResultTable('blackPets'));
// -> {felix: {species: 'cat'}, cujo: {species: 'dog'}}

// A joined table query:
queries.setQueryDefinition('petOwners', 'pets', ({select, join}) => {
  select('owners', 'name').as('owner');
  join('owners', 'ownerId');
});
console.log(queries.getResultTable('petOwners'));
// -> {fido: {owner: 'Alice'}, felix: {owner: 'Bob'}, cujo: {owner: 'Carol'}}

// A grouped query:
queries.setQueryDefinition(
  'colorPrice',
  'pets',
  ({select, join, group}) => {
    select('color');
    select('species', 'price');
    join('species', 'species');
    group('price', 'avg');
  },
);
console.log(queries.getResultTable('colorPrice'));
// -> {"1": {color: 'black', price: 4.5}, "0": {color: 'brown', price: 5}}
console.log(queries.getResultSortedRowIds('colorPrice', 'price', true));
// -> ["0", "1"]

const listenerId = queries.addResultTableListener('colorPrice', () => {
  console.log('Average prices per color changed');
  console.log(queries.getResultTable('colorPrice'));
  console.log(queries.getResultSortedRowIds('colorPrice', 'price', true));
});

store.setRow('pets', 'lowly', {species: 'worm', color: 'brown'});
// -> 'Average prices per color changed'
// -> {"0": {color: 'brown', price: 3}, "1": {color: 'black', price: 4.5}}
// -> ["1", "0"]

queries.delListener(listenerId);
queries.destroy();
See also
Since

v2.0.0

Getter methods

This is the collection of getter methods within the Queries interface. There are 4 getter methods in total.

getStore

The getStore method returns a reference to the underlying Store that is backing this Queries object.

getStore(): Store
returnsStore

A reference to the Store.

Example

This example creates a Queries object against a newly-created Store and then gets its reference in order to update its data.

import {createQueries, createStore} from 'tinybase';

const queries = createQueries(createStore());
queries.setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
queries
  .getStore()
  .setRow('pets', 'fido', {species: 'dog', color: 'brown'});
console.log(queries.getResultTable('dogColors'));
// -> {fido: {color: 'brown'}}
Since

v2.0.0

getTableId

The getTableId method returns the Id of the underlying Table that is backing a query.

getTableId(queryId: string): undefined | string
TypeDescription
queryIdstring

The Id of a query.

returnsundefined | string

The Id of the Table backing the query, or undefined.

If the query Id is invalid, the method returns undefined.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent definition) to get the underlying Table Id.

import {createQueries, createStore} from 'tinybase';

const queries = createQueries(createStore()).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.getTableId('dogColors'));
// -> 'pets'
console.log(queries.getTableId('catColors'));
// -> undefined
Since

v2.0.0

getQueryIds

The getQueryIds method returns an array of the query Ids registered with this Queries object.

getQueryIds(): Ids
returnsIds

An array of Ids.

Example

This example creates a Queries object with two definitions, and then gets the Ids of the definitions.

import {createQueries, createStore} from 'tinybase';

const queries = createQueries(createStore())
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'cat');
  });

console.log(queries.getQueryIds());
// -> ['dogColors', 'catColors']
Since

v2.0.0

hasQuery

The hasQuery method returns a boolean indicating whether a given query exists in the Queries object.

hasQuery(queryId: string): boolean
TypeDescription
queryIdstring

The Id of a possible query in the Queries object.

returnsboolean

Whether a query with that Id exists.

Example

This example shows two simple query existence checks.

import {createQueries, createStore} from 'tinybase';

const queries = createQueries(createStore()).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.hasQuery('dogColors'));
// -> true
console.log(queries.hasQuery('catColors'));
// -> false
Since

v2.0.0

Result methods

This is the collection of result methods within the Queries interface. There are 11 result methods in total.

getResultTable

The getResultTable method returns an object containing the entire data of the ResultTable of the given query.

getResultTable(queryId: string): ResultTable
TypeDescription
queryIdstring

The Id of a query.

returnsResultTable

An object containing the entire data of the ResultTable of the query.

This has the same behavior as a Store's getTable method. For example, if the query Id is invalid, the method returns an empty object. Similarly, it returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the query results themselves.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent definition) to get the ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.getResultTable('dogColors'));
// -> {fido: {color: 'brown'}, cujo: {color: 'black'}}

console.log(queries.getResultTable('catColors'));
// -> {}
Since

v2.0.0

getResultTableCellIds

The getResultTableCellIds method returns the Ids of every ResultCell used across the ResultTable of the given query.

getResultTableCellIds(queryId: string): Ids
TypeDescription
queryIdstring

The Id of a query.

returnsIds

An array of the Ids of every ResultCell used across the ResultTable of the query.

This has the same behavior as a Store's getTableCellIds method. For example, if the query Id is invalid, the method returns an empty array. Similarly, it returns a copy of, rather than a reference to the list of Ids, so changes made to the list object are not made to the query results themselves.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent definition) to get the ResultCell Ids.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black', legs: 4},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    select('legs');
    where('species', 'dog');
  },
);

console.log(queries.getResultTableCellIds('dogColors'));
// -> ['color', 'legs']

console.log(queries.getResultTableCellIds('catColors'));
// -> []
Since

v4.1.0

hasResultTable

The hasResultTable method returns a boolean indicating whether a given ResultTable exists.

hasResultTable(queryId: string): boolean
TypeDescription
queryIdstring

The Id of a possible query.

returnsboolean

Whether a ResultTable for that query Id exists.

Example

This example shows two simple ResultTable existence checks.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.hasResultTable('dogColors'));
// -> true
console.log(queries.hasResultTable('catColors'));
// -> false
Since

v2.0.0

getResultRowIds

The getResultRowIds method returns the Ids of every ResultRow in the ResultTable of the given query.

getResultRowIds(queryId: string): Ids
TypeDescription
queryIdstring

The Id of a query.

returnsIds

An array of the Ids of every ResultRow in the result of the query.

This has the same behavior as a Store's getRowIds method. For example, if the query Id is invalid, the method returns an empty array. Similarly, it returns a copy of, rather than a reference to the list of Ids, so changes made to the list object are not made to the query results themselves.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent definition) to get the ResultRow Ids.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.getResultRowIds('dogColors'));
// -> ['fido', 'cujo']

console.log(queries.getResultRowIds('catColors'));
// -> []
Since

v2.0.0

getResultSortedRowIds

The getResultSortedRowIds method returns the Ids of every ResultRow in the ResultTable of the given query, sorted according to the values in a specified ResultCell.

getResultSortedRowIds(
  queryId: string,
  cellId?: string,
  descending?: boolean,
  offset?: number,
  limit?: number,
): Ids
TypeDescription
queryIdstring

The Id of a query.

cellId?string

The Id of the ResultCell whose values are used for the sorting, or undefined to by sort the ResultRow Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of ResultRow Ids to skip for pagination purposes, if any.

limit?number

The maximum number of ResultRow Ids to return, or undefined for all.

returnsIds

An array of the sorted Ids of every ResultRow in the result of the query.

This has the same behavior as a Store's getSortedRowIds method. For example, if the query Id is invalid, the method returns an empty array. Similarly, the sorting of the rows is alphanumeric, and you can indicate whether it should be in descending order. The offset and limit parameters are used to paginate results, but default to 0 and undefined to return all available ResultRow Ids if not specified.

Note that every call to this method will perform the sorting afresh - there is no caching of the results - and so you are advised to memoize the results yourself, especially when the ResultTable is large. For a performant approach to tracking the sorted ResultRow Ids when they change, use the addResultSortedRowIdsListener method.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent definition) to get the ResultRow Ids.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.getResultSortedRowIds('dogColors', 'color'));
// -> ['cujo', 'fido']

console.log(queries.getResultSortedRowIds('catColors', 'color'));
// -> []
Since

v2.0.0

getResultRow

The getResultRow method returns an object containing the entire data of a single ResultRow in the ResultTable of the given query.

getResultRow(
  queryId: string,
  rowId: string,
): ResultRow
TypeDescription
queryIdstring

The Id of a query.

rowIdstring

The Id of the ResultRow in the ResultTable.

returnsResultRow

An object containing the entire data of the ResultRow in the ResultTable of the query.

This has the same behavior as a Store's getRow method. For example, if the query or ResultRow Id is invalid, the method returns an empty object. Similarly, it returns a copy of, rather than a reference to the underlying data, so changes made to the returned object are not made to the query results themselves.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent ResultRow Id) to get the ResultRow.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.getResultRow('dogColors', 'fido'));
// -> {color: 'brown'}

console.log(queries.getResultRow('dogColors', 'felix'));
// -> {}
Since

v2.0.0

getResultRowCount

The getResultRowCount method returns the count of the ResultRow objects in the ResultTable of the given query.

getResultRowCount(queryId: string): number
TypeDescription
queryIdstring

The Id of a query.

returnsnumber

The number of ResultRow objects in the result of the query.

This has the same behavior as a Store's getRowCount method. For example, if the query Id is invalid, the method returns zero.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent definition) to get the ResultRow count.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.getResultRowCount('dogColors'));
// -> 2

console.log(queries.getResultRowCount('catColors'));
// -> 0
Since

v4.1.0

hasResultRow

The hasResultRow method returns a boolean indicating whether a given ResultRow exists.

hasResultRow(
  queryId: string,
  rowId: string,
): boolean
TypeDescription
queryIdstring

The Id of a possible query.

rowIdstring

The Id of a possible ResultRow.

returnsboolean

Whether a ResultRow for that Id exists.

Example

This example shows two simple ResultRow existence checks.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.hasResultRow('dogColors', 'fido'));
// -> true
console.log(queries.hasResultRow('dogColors', 'felix'));
// -> false
Since

v2.0.0

getResultCellIds

The getResultCellIds method returns the Ids of every ResultCell in a given ResultRow, in the ResultTable of the given query.

getResultCellIds(
  queryId: string,
  rowId: string,
): Ids
TypeDescription
queryIdstring

The Id of a query.

rowIdstring

The Id of the ResultRow in the ResultTable.

returnsIds

An array of the Ids of every ResultCell in the ResultRow in the result of the query.

This has the same behavior as a Store's getCellIds method. For example, if the query Id or ResultRow Id is invalid, the method returns an empty array. Similarly, it returns a copy of, rather than a reference to the list of Ids, so changes made to the list object are not made to the query results themselves.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent ResultRow Id) to get the ResultCell Ids.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.getResultCellIds('dogColors', 'fido'));
// -> ['color']

console.log(queries.getResultCellIds('dogColors', 'felix'));
// -> []
Since

v2.0.0

getResultCell

The getResultCell method returns the value of a single ResultCell in a given ResultRow, in the ResultTable of the given query.

getResultCell(
  queryId: string,
  rowId: string,
  cellId: string,
): ResultCellOrUndefined
TypeDescription
queryIdstring

The Id of a query.

rowIdstring

The Id of the ResultRow in the ResultTable.

cellIdstring

The Id of the ResultCell in the ResultRow.

returnsResultCellOrUndefined

The value of the ResultCell, or undefined.

This has the same behavior as a Store's getCell method. For example, if the query, or ResultRow, or ResultCell Id is invalid, the method returns undefined.

Example

This example creates a Queries object, a single query definition, and then calls this method on it (as well as a non-existent ResultCell Id) to get the ResultCell.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.getResultCell('dogColors', 'fido', 'color'));
// -> 'brown'

console.log(queries.getResultCell('dogColors', 'fido', 'species'));
// -> undefined
Since

v2.0.0

hasResultCell

The hasResultCell method returns a boolean indicating whether a given ResultCell exists.

hasResultCell(
  queryId: string,
  rowId: string,
  cellId: string,
): boolean
TypeDescription
queryIdstring

The Id of a possible query.

rowIdstring

The Id of a possible ResultRow.

cellIdstring

The Id of a possible ResultCell.

returnsboolean

Whether a ResultCell for that Id exists.

Example

This example shows two simple ResultRow existence checks.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

console.log(queries.hasResultCell('dogColors', 'fido', 'color'));
// -> true
console.log(queries.hasResultCell('dogColors', 'fido', 'species'));
// -> false
Since

v2.0.0

Listener methods

This is the collection of listener methods within the Queries interface. There are 10 listener methods in total.

addResultTableCellIdsListener

The addResultTableCellIdsListener method registers a listener function with the Queries object that will be called whenever the Cell Ids that appear anywhere in a ResultTable change.

addResultTableCellIdsListener(
  queryId: IdOrNull,
  listener: ResultTableCellIdsListener,
): string
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

listenerResultTableCellIdsListener

The function that will be called whenever the Cell Ids that appear anywhere in the ResultTable change.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ResultTableCellIdsListener function, and will be called with a reference to the Queries object and the Id of the ResultTable that changed (which is also the query Id).

By default, such a listener is only called when a Cell Id is added to, or removed from, the ResultTable. To listen to all changes in the ResultTable, use the addResultTableListener method.

You can either listen to a single ResultTable (by specifying a query Id as the method's first parameter) or changes to any ResultTable (by providing a null wildcard).

Examples

This example registers a listener that responds to any change to the Cell Ids of a specific ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColorsAndLegs',
  'pets',
  ({select, where}) => {
    select('color');
    select('legs');
    where('species', 'dog');
  },
);

const listenerId = queries.addResultTableCellIdsListener(
  'dogColorsAndLegs',
  (queries) => {
    console.log(`Cell Ids for dogColorsAndLegs result table changed`);
    console.log(queries.getResultTableCellIds('dogColorsAndLegs'));
  },
);

store.setCell('pets', 'cujo', 'legs', 4);
// -> 'Cell Ids for dogColorsAndLegs result table changed'
// -> ['color', 'legs']

store.delListener(listenerId);

This example registers a listener that responds to any change to the ResultCell Ids of any ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black', legs: 4},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store)
  .setQueryDefinition('dogColorsAndLegs', 'pets', ({select, where}) => {
    select('color');
    select('legs');
    where('species', 'dog');
  })
  .setQueryDefinition('catColorsAndLegs', 'pets', ({select, where}) => {
    select('color');
    select('legs');
    where('species', 'cat');
  });

const listenerId = queries.addResultTableCellIdsListener(
  null,
  (queries, tableId) => {
    console.log(`Cell Ids for ${tableId} result table changed`);
  },
);

store.setCell('pets', 'cujo', 'legs', 4);
// -> 'Cell Ids for dogColorsAndLegs result table changed'
store.delCell('pets', 'felix', 'legs');
// -> 'Cell Ids for catColorsAndLegs result table changed'

store.delListener(listenerId);
Since

v2.0.0

addResultTableListener

The addResultTableListener method registers a listener function with the Queries object that will be called whenever data in a ResultTable changes.

addResultTableListener(
  queryId: IdOrNull,
  listener: ResultTableListener,
): string
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

listenerResultTableListener

The function that will be called whenever data in the matching ResultTable changes.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ResultTableListener function, and will be called with a reference to the Queries object, the Id of the ResultTable that changed (which is also the query Id), and a GetResultCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single ResultTable (by specifying a query Id as the method's first parameter) or changes to any ResultTable (by providing a null wildcard).

Examples

This example registers a listener that responds to any changes to a specific ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

const listenerId = queries.addResultTableListener(
  'dogColors',
  (queries, tableId, getCellChange) => {
    console.log('dogColors result table changed');
    console.log(getCellChange('dogColors', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'dogColors result table changed'
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store)
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'cat');
  });

const listenerId = queries.addResultTableListener(
  null,
  (queries, tableId) => {
    console.log(`${tableId} result table changed`);
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'dogColors result table changed'
store.setCell('pets', 'felix', 'color', 'tortoiseshell');
// -> 'catColors result table changed'

store.delListener(listenerId);
Since

v2.0.0

addResultRowIdsListener

The addResultRowIdsListener method registers a listener function with the Queries object that will be called whenever the ResultRow Ids in a ResultTable change.

addResultRowIdsListener(
  queryId: IdOrNull,
  listener: ResultRowIdsListener,
): string
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

listenerResultRowIdsListener

The function that will be called whenever the ResultRow Ids in the ResultTable change.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ResultRowIdsListener function, and will be called with a reference to the Queries object and the Id of the ResultTable that changed (which is also the query Id).

By default, such a listener is only called when a ResultRow is added to, or removed from, the ResultTable. To listen to all changes in the ResultTable, use the addResultTableListener method.

You can either listen to a single ResultTable (by specifying a query Id as the method's first parameter) or changes to any ResultTable (by providing a null wildcard).

Examples

This example registers a listener that responds to any change to the ResultRow Ids of a specific ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

const listenerId = queries.addResultRowIdsListener(
  'dogColors',
  (queries) => {
    console.log(`Row Ids for dogColors result table changed`);
    console.log(queries.getResultRowIds('dogColors'));
  },
);

store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
// -> 'Row Ids for dogColors result table changed'
// -> ['fido', 'cujo', 'rex']

store.delListener(listenerId);

This example registers a listener that responds to any change to the ResultRow Ids of any ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store)
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'cat');
  });

const listenerId = queries.addResultRowIdsListener(
  null,
  (queries, tableId) => {
    console.log(`Row Ids for ${tableId} result table changed`);
  },
);

store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
// -> 'Row Ids for dogColors result table changed'
store.setRow('pets', 'tom', {species: 'cat', color: 'gray'});
// -> 'Row Ids for catColors result table changed'

store.delListener(listenerId);
Since

v2.0.0

addResultSortedRowIdsListener

The addResultSortedRowIdsListener method registers a listener function with the Queries object that will be called whenever sorted (and optionally, paginated) ResultRow Ids in a ResultTable change.

addResultSortedRowIdsListener(
  queryId: string,
  cellId: undefined | string,
  descending: boolean,
  offset: number,
  limit: undefined | number,
  listener: ResultSortedRowIdsListener,
): string
TypeDescription
queryIdstring

The Id of the query to listen to.

cellIdundefined | string

The Id of the ResultCell whose values are used for the sorting, or undefined to by sort the ResultRow Id itself.

descendingboolean

Whether the sorting should be in descending order.

offsetnumber

The number of ResultRow Ids to skip for pagination purposes, if any.

limitundefined | number

The maximum number of ResultRow Ids to return, or undefined for all.

listenerResultSortedRowIdsListener

The function that will be called whenever the sorted ResultRow Ids in the ResultTable change.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ResultSortedRowIdsListener function, and will be called with a reference to the Queries object, the Id of the ResultTable whose ResultRow Ids sorting changed (which is also the query Id), the ResultCell Id being used to sort them, whether descending or not, and the offset and limit of the number of Ids returned, for pagination purposes. It also receives the sorted array of Ids itself, so that you can use them in the listener without the additional cost of an explicit call to the getResultSortedRowIds method.

Such a listener is called when a ResultRow is added or removed, but also when a value in the specified ResultCell (somewhere in the ResultTable) has changed enough to change the sorting of the ResultRow Ids.

Unlike most other listeners, you cannot provide wildcards (due to the cost of detecting changes to the sorting). You can only listen to a single specified ResultTable, sorted by a single specified ResultCell.

The sorting of the rows is alphanumeric, and you can indicate whether it should be in descending order. The offset and limit parameters are used to paginate results, but default to 0 and undefined to return all available ResultRow Ids if not specified.

Examples

This example registers a listener that responds to any change to the sorted ResultRow Ids of a specific ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

const listenerId = queries.addResultSortedRowIdsListener(
  'dogColors',
  'color',
  false,
  0,
  undefined,
  (queries, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`Sorted row Ids for dogColors result table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getResultSortedRowIds again
  },
);

store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
// -> 'Sorted row Ids for dogColors result table changed'
// -> ['cujo', 'fido', 'rex']

store.delListener(listenerId);

This example registers a listener that responds to any change to the sorted ResultRow Ids of a specific ResultTable. The ResultRow Ids are sorted by their own value, since the cellId parameter is explicitly undefined.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);
console.log(queries.getResultSortedRowIds('dogColors', undefined));
// -> ['cujo', 'fido']

const listenerId = queries.addResultSortedRowIdsListener(
  'dogColors',
  undefined,
  false,
  0,
  undefined,
  (queries, tableId, cellId, descending, offset, limit, sortedRowIds) => {
    console.log(`Sorted row Ids for dogColors result table changed`);
    console.log(sortedRowIds);
    // ^ cheaper than calling getSortedRowIds again
  },
);

store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
// -> 'Sorted row Ids for dogColors result table changed'
// -> ['cujo', 'fido', 'rex']

store.delListener(listenerId);
Since

v2.0.0

addResultRowCountListener

The addResultRowCountListener method registers a listener function with the Queries object that will be called whenever the count of ResultRow objects in a ResultTable changes.

addResultRowCountListener(
  queryId: IdOrNull,
  listener: ResultRowCountListener,
): string
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

listenerResultRowCountListener

The function that will be called whenever the number of ResultRow objects in the ResultTable change.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ResultRowCountListener function, and will be called with a reference to the Queries object, the Id of the ResultTable that changed (which is also the query Id), and the number of ResultRow objects in th ResultTable.

You can either listen to a single ResultTable (by specifying a query Id as the method's first parameter) or changes to any ResultTable (by providing a null wildcard).

Examples

This example registers a listener that responds to a change in the number of ResultRow objects in a specific ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

const listenerId = queries.addResultRowCountListener(
  'dogColors',
  (queries, tableId, count) => {
    console.log(
      'Row count for dogColors result table changed to ' + count,
    );
  },
);

store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
// -> 'Row count for dogColors result table changed to 3'

store.delListener(listenerId);

This example registers a listener that responds to a change in the number of ResultRow objects any ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store)
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'cat');
  });

const listenerId = queries.addResultRowCountListener(
  null,
  (queries, tableId, count) => {
    console.log(
      `Row count for ${tableId} result table changed to ${count}`,
    );
  },
);

store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
// -> 'Row count for dogColors result table changed to 3'
store.setRow('pets', 'tom', {species: 'cat', color: 'gray'});
// -> 'Row count for catColors result table changed to 2'

store.delListener(listenerId);
Since

v4.1.0

addResultRowListener

The addResultRowListener method registers a listener function with the Queries object that will be called whenever data in a ResultRow changes.

addResultRowListener(
  queryId: IdOrNull,
  rowId: IdOrNull,
  listener: ResultRowListener,
): string
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the ResultRow to listen to, or null as a wildcard.

listenerResultRowListener

The function that will be called whenever data in the matching ResultRow changes.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ResultRowListener function, and will be called with a reference to the Queries object, the Id of the ResultTable that changed (which is also the query Id), and a GetResultCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single ResultRow (by specifying a query Id and ResultRow Id as the method's first two parameters) or changes to any ResultRow (by providing null wildcards).

Both, either, or neither of the queryId and rowId parameters can be wildcarded with null. You can listen to a specific ResultRow in a specific query, any ResultRow in a specific query, a specific ResultRow in any query, or any ResultRow in any query.

Examples

This example registers a listener that responds to any changes to a specific ResultRow.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

const listenerId = queries.addResultRowListener(
  'dogColors',
  'fido',
  (queries, tableId, rowId, getCellChange) => {
    console.log('fido row in dogColors result table changed');
    console.log(getCellChange('dogColors', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'fido row in dogColors result table changed'
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any ResultRow.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store)
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'cat');
  });

const listenerId = queries.addResultRowListener(
  null,
  null,
  (queries, tableId, rowId) => {
    console.log(`${rowId} row in ${tableId} result table changed`);
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'fido row in dogColors result table changed'
store.setCell('pets', 'felix', 'color', 'tortoiseshell');
// -> 'felix row in catColors result table changed'

store.delListener(listenerId);
Since

v2.0.0

addResultCellIdsListener

The addResultCellIdsListener method registers a listener function with the Queries object that will be called whenever the ResultCell Ids in a ResultRow change.

addResultCellIdsListener(
  queryId: IdOrNull,
  rowId: IdOrNull,
  listener: ResultCellIdsListener,
): string
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the ResultRow to listen to, or null as a wildcard.

listenerResultCellIdsListener

The function that will be called whenever the ResultCell Ids in the ResultRow change.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ResultCellIdsListener function, and will be called with a reference to the Queries object, the Id of the ResultTable (which is also the query Id), and the Id of the ResultRow that changed.

Such a listener is only called when a ResultCell is added to, or removed from, the ResultRow. To listen to all changes in the ResultRow, use the addResultRowListener method.

You can either listen to a single ResultRow (by specifying the query Id and ResultRow Id as the method's first two parameters) or changes to any ResultRow (by providing null wildcards).

Both, either, or neither of the queryId and rowId parameters can be wildcarded with null. You can listen to a specific ResultRow in a specific query, any ResultRow in a specific query, a specific ResultRow in any query, or any ResultRow in any query.

Examples

This example registers a listener that responds to any change to the ResultCell Ids of a specific ResultRow.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    select('price');
    where('species', 'dog');
  },
);

const listenerId = queries.addResultCellIdsListener(
  'dogColors',
  'fido',
  (queries) => {
    console.log(`Cell Ids for fido row in dogColors result table changed`);
    console.log(queries.getResultCellIds('dogColors', 'fido'));
  },
);

store.setCell('pets', 'fido', 'price', 5);
// -> 'Cell Ids for fido row in dogColors result table changed'
// -> ['color', 'price']

store.delListener(listenerId);

This example registers a listener that responds to any change to the ResultCell Ids of any ResultRow.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store)
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    select('price');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    select('purrs');
    where('species', 'cat');
  });

const listenerId = queries.addResultCellIdsListener(
  null,
  null,
  (queries, tableId, rowId) => {
    console.log(
      `Cell Ids for ${rowId} row in ${tableId} result table changed`,
    );
  },
);

store.setCell('pets', 'fido', 'price', 5);
// -> 'Cell Ids for fido row in dogColors result table changed'
store.setCell('pets', 'felix', 'purrs', true);
// -> 'Cell Ids for felix row in catColors result table changed'

store.delListener(listenerId);
Since

v2.0.0

addResultCellListener

The addResultCellListener method registers a listener function with the Queries object that will be called whenever data in a ResultCell changes.

addResultCellListener(
  queryId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: ResultCellListener,
): string
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the ResultRow to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the ResultCell to listen to, or null as a wildcard.

listenerResultCellListener

The function that will be called whenever data in the matching ResultCell changes.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ResultCellListener function, and will be called with a reference to the Queries object, the Id of the ResultTable that changed (which is also the query Id), the Id of the ResultRow that changed, the Id of the ResultCell that changed, the new ResultCell value, the old ResultCell value, and a GetResultCellChange function in case you need to inspect any changes that occurred.

You can either listen to a single ResultRow (by specifying a query Id, ResultRow Id, and ResultCell Id as the method's first three parameters) or changes to any ResultCell (by providing null wildcards).

All, some, or none of the queryId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific ResultCell in a specific ResultRow in a specific query, any ResultCell in any ResultRow in any query, for example - or every other combination of wildcards.

Examples

This example registers a listener that responds to any changes to a specific ResultCell.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

const listenerId = queries.addResultCellListener(
  'dogColors',
  'fido',
  'color',
  (queries, tableId, rowId, cellId, newCell, oldCell, getCellChange) => {
    console.log(
      'color cell in fido row in dogColors result table changed',
    );
    console.log([oldCell, newCell]);
    console.log(getCellChange('dogColors', 'fido', 'color'));
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'color cell in fido row in dogColors result table changed'
// -> ['brown', 'walnut']
// -> [true, 'brown', 'walnut']

store.delListener(listenerId);

This example registers a listener that responds to any changes to any ResultCell.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown', price: 5},
  felix: {species: 'cat', color: 'black', price: 4},
  cujo: {species: 'dog', color: 'black', price: 5},
});

const queries = createQueries(store)
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    select('price');
    where('species', 'cat');
  });

const listenerId = queries.addResultCellListener(
  null,
  null,
  null,
  (queries, tableId, rowId, cellId) => {
    console.log(
      `${cellId} cell in ${rowId} row in ${tableId} result table changed`,
    );
  },
);

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'color cell in fido row in dogColors result table changed'
store.setCell('pets', 'felix', 'price', 3);
// -> 'price cell in felix row in catColors result table changed'

store.delListener(listenerId);
Since

v2.0.0

addQueryIdsListener

The addQueryIdsListener method registers a listener function with the Queries object that will be called whenever an Query definition is added or removed.

addQueryIdsListener(listener: QueryIdsListener): string
TypeDescription
listenerQueryIdsListener

The function that will be called whenever a Query definition is added or removed.

returnsstring

The provided listener is a QueryIdsListener function, and will be called with a reference to the Queries object.

Example

This example creates a Store, a Queries object, and then registers a listener that responds to the addition and the removal of a Query definition.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store);
const listenerId = queries.addQueryIdsListener((queries) => {
  console.log(queries.getQueryIds());
});

queries.setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
// -> ['dogColors']
queries.delQueryDefinition('dogColors');
// -> []

queries.delListener(listenerId);
Since

v4.1.0

delListener

The delListener method removes a listener that was previously added to the Queries object.

delListener(listenerId: string): Queries
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsQueries

A reference to the Queries object.

Use the Id returned by the addMetricListener method. Note that the Queries object may re-use this Id for future listeners added to it.

Example

This example creates a Store, a Queries object, registers a listener, and then removes it.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const queries = createQueries(store).setQueryDefinition(
  'species',
  'pets',
  ({select}) => {
    select('species');
  },
);

const listenerId = queries.addResultTableListener('species', () =>
  console.log('species result changed'),
);

store.setCell('pets', 'ed', 'species', 'horse');
// -> 'species result changed'

queries.delListener(listenerId);

store.setCell('pets', 'molly', 'species', 'cow');
// -> undefined
// The listener is not called.
Since

v2.0.0

Configuration methods

This is the collection of configuration methods within the Queries interface. There are only two configuration methods, delQueryDefinition and setQueryDefinition.

delQueryDefinition

The delQueryDefinition method removes an existing query definition.

delQueryDefinition(queryId: string): Queries
TypeDescription
queryIdstring

The Id of the query to remove.

returnsQueries

A reference to the Queries object.

Example

This example creates a Store, creates a Queries object, defines a simple query, and then removes it.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store);
queries.setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
console.log(queries.getQueryIds());
// -> ['dogColors']

queries.delQueryDefinition('dogColors');
console.log(queries.getQueryIds());
// -> []
Since

v2.0.0

setQueryDefinition

The setQueryDefinition method lets you set the definition of a query.

setQueryDefinition(
  queryId: string,
  tableId: string,
  query: (keywords: {
    select: Select;
    join: Join;
    where: Where;
    group: Group;
    having: Having;
  }) => void,
): Queries
TypeDescription
queryIdstring

The Id of the query to define.

tableIdstring

The Id of the root Table the query will be based on.

query(keywords: { select: Select; join: Join; where: Where; group: Group; having: Having; }) => void

A callback which can take a keywords object and which uses the functions it contains to define the query.

returnsQueries

A reference to the Queries object.

Every query definition is identified by a unique Id, and if you re-use an existing Id with this method, the previous definition is overwritten.

A query provides a tabular result formed from each Row within a root Table. The definition must specify this Table (by its Id) to be aggregated. Other Tables can be joined to that using Join clauses.

The third query parameter is a callback that you provide to define the query. That callback is provided with a keywords object that contains the functions you use to define the query, like select, join, and so on. You can see how that is used in the simple example below. The following five clause types are supported:

  • The Select type describes a function that lets you specify a Cell or calculated value for including into the query's result.
  • The Join type describes a function that lets you specify a Cell or calculated value to join the main query Table to others, by Row Id.
  • The Where type describes a function that lets you specify conditions to filter results, based on the underlying Cells of the main or joined Tables.
  • The Group type describes a function that lets you specify that the values of a Cell in multiple ResultRows should be aggregated together.
  • The Having type describes a function that lets you specify conditions to filter results, based on the grouped Cells resulting from a Group clause.

Full documentation and examples are provided in the sections for each of those clause types.

Additionally, you can use the getResultSortedRowIds method and addResultSortedRowIdsListener method to sort and paginate the results.

Example

This example creates a Store, creates a Queries object, and defines a simple query to select just one column from the Table, for each Row where the species Cell matches as certain value.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store);
queries.setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});

console.log(queries.getResultTable('dogColors'));
// -> {fido: {color: 'brown'}, cujo: {color: 'black'}}
Since

v2.0.0

Iterator methods

This is the collection of iterator methods within the Queries interface. There are 4 iterator methods in total.

forEachResultTable

The forEachResultTable method takes a function that it will then call for each ResultTable in the Queries object.

forEachResultTable(tableCallback: ResultTableCallback): void
TypeDescription
tableCallbackResultTableCallback

The function that should be called for every query's ResultTable.

returnsvoid

This has no return value.

This method is useful for iterating over all the ResultTables of the queries in a functional style. The tableCallback parameter is a ResultTableCallback function that will be called with the Id of each ResultTable, and with a function that can then be used to iterate over each ResultRow of the ResultTable, should you wish.

Example

This example iterates over each query's ResultTable in a Queries object.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store)
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'cat');
  });

queries.forEachResultTable((queryId, forEachRow) => {
  console.log(queryId);
  forEachRow((rowId) => console.log(`- ${rowId}`));
});
// -> 'dogColors'
// -> '- fido'
// -> '- cujo'
// -> 'catColors'
// -> '- felix'
Since

v2.0.0

forEachResultRow

The forEachResultRow method takes a function that it will then call for each ResultRow in the ResultTable of a query.

forEachResultRow(
  queryId: string,
  rowCallback: ResultRowCallback,
): void
TypeDescription
queryIdstring

The Id of a query.

rowCallbackResultRowCallback

The function that should be called for every ResultRow of the query's ResultTable.

returnsvoid

This has no return value.

This method is useful for iterating over each ResultRow of the ResultTable of the query in a functional style. The rowCallback parameter is a ResultRowCallback function that will be called with the Id of each ResultRow, and with a function that can then be used to iterate over each ResultCell of the ResultRow, should you wish.

Example

This example iterates over each ResultRow in a query's ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);

queries.forEachResultRow('dogColors', (rowId, forEachCell) => {
  console.log(rowId);
  forEachCell((cellId) => console.log(`- ${cellId}`));
});
// -> 'fido'
// -> '- color'
// -> 'cujo'
// -> '- color'
Since

v2.0.0

forEachResultCell

The forEachResultCell method takes a function that it will then call for each ResultCell in the ResultRow of a query.

forEachResultCell(
  queryId: string,
  rowId: string,
  cellCallback: ResultCellCallback,
): void
TypeDescription
queryIdstring

The Id of a query.

rowIdstring

The Id of a ResultRow in the query's ResultTable.

cellCallbackResultCellCallback

The function that should be called for every ResultCell of the query's ResultRow.

returnsvoid

This has no return value.

This method is useful for iterating over each ResultCell of the ResultRow of the query in a functional style. The cellCallback parameter is a ResultCellCallback function that will be called with the Id and value of each ResultCell.

Example

This example iterates over each ResultCell in a query's ResultRow.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('species');
    select('color');
    where('species', 'dog');
  },
);

queries.forEachResultCell('dogColors', 'fido', (cellId, cell) => {
  console.log(`${cellId}: ${cell}`);
});
// -> 'species: dog'
// -> 'color: brown'
Since

v2.0.0

forEachQuery

The forEachQuery method takes a function that it will then call for each Query in the Queries object.

forEachQuery(queryCallback: QueryCallback): void
TypeDescription
queryCallbackQueryCallback

The function that should be called for every query.

returnsvoid

This has no return value.

This method is useful for iterating over all the queries in a functional style. The queryCallback parameter is a QueryCallback function that will be called with the Id of each query.

Example

This example iterates over each query in a Queries object.

import {createQueries, createStore} from 'tinybase';

const queries = createQueries(createStore())
  .setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('catColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'cat');
  });

queries.forEachQuery((queryId) => {
  console.log(queryId);
});
// -> 'dogColors'
// -> 'catColors'
Since

v2.0.0

Lifecycle methods

This is the collection of lifecycle methods within the Queries interface. There is only one method, destroy.

destroy

The destroy method should be called when this Queries object is no longer used.

destroy(): void

This guarantees that all of the listeners that the object registered with the underlying Store are removed and it can be correctly garbage collected.

Example

This example creates a Store, adds a Queries object with a definition (that registers a RowListener with the underlying Store), and then destroys it again, removing the listener.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const queries = createQueries(store);
queries.setQueryDefinition('species', 'species', ({select}) => {
  select('species');
});
console.log(store.getListenerStats().row);
// -> 1

queries.destroy();

console.log(store.getListenerStats().row);
// -> 0
Since

v2.0.0

Development methods

This is the collection of development methods within the Queries interface. There is only one method, getListenerStats.

getListenerStats

The getListenerStats method provides a set of statistics about the listeners registered with the Queries object, and is used for debugging purposes.

getListenerStats(): QueriesListenerStats
returnsQueriesListenerStats

A QueriesListenerStats object containing Queries listener statistics.

The method is intended to be used during development to ensure your application is not leaking listener registrations, for example.

Example

This example gets the listener statistics of a Queries object.

import {createQueries, createStore} from 'tinybase';

const store = createStore();
const queries = createQueries(store);
queries.addResultTableListener(null, () => console.log('Result changed'));

console.log(queries.getListenerStats().table);
// -> 1
console.log(queries.getListenerStats().row);
// -> 0
Since

v2.0.0

Functions

There is one function, createQueries, within the queries module.

createQueries

The createQueries function creates a Queries object, and is the main entry point into the queries module.

createQueries(store: Store): Queries
TypeDescription
storeStore

The Store for which to register query definitions.

returnsQueries

A reference to the new Queries object.

A given Store can only have one Queries object associated with it. If you call this function twice on the same Store, your second call will return a reference to the Queries object created by the first.

Examples

This example creates a Queries object.

import {createQueries, createStore} from 'tinybase';

const store = createStore();
const queries = createQueries(store);
console.log(queries.getQueryIds());
// -> []

This example creates a Queries object, and calls the method a second time for the same Store to return the same object.

import {createQueries, createStore} from 'tinybase';

const store = createStore();
const queries1 = createQueries(store);
const queries2 = createQueries(store);
console.log(queries1 === queries2);
// -> true
Since

v2.0.0

Type Aliases

These are the type aliases within the queries module.

Definition type aliases

This is the collection of definition type aliases within the queries module. There are 8 definition type aliases in total.

Group

The Group type describes a function that lets you specify that the values of a Cell in multiple ResultRows should be aggregated together.

(
  selectedCellId: Id,
  aggregate: "count" | "sum" | "avg" | "min" | "max" | Aggregate,
  aggregateAdd?: AggregateAdd,
  aggregateRemove?: AggregateRemove,
  aggregateReplace?: AggregateReplace,
): GroupedAs
TypeDescription
selectedCellIdId

The Id of the Cell to aggregate. If the underlying Cell was selected 'as' a different Id, that should instead be used.

aggregate"count" | "sum" | "avg" | "min" | "max" | Aggregate

Either a string representing one of a set of common aggregation techniques ('count', 'sum', 'avg', 'min', or 'max'), or a function that aggregates Cell values from each Row to create the aggregate's overall.

aggregateAdd?AggregateAdd

A function that can be used to optimize a custom Aggregate by providing a shortcut for when a single value is added to the input values - for example, when a Row is added to the Table.

aggregateRemove?AggregateRemove

A function that can be used to optimize a custom Aggregate by providing a shortcut for when a single value is removed from the input values - for example, when a Row is removed from the Table.

aggregateReplace?AggregateReplace

A function that can be used to optimize a custom Aggregate by providing a shortcut for when a single value in the input values is replaced with another - for example, when a Row is updated.

returnsGroupedAs

The Group function is provided to the third query parameter of the setQueryDefinition method. When called, it should refer to a Cell Id (or aliased Id) specified in one of the Select functions, and indicate how the values should be aggregated.

This is applied after any joins or where-based filtering.

If you provide a Group for every Select, the result will be a single Row with every Cell having been aggregated. If you provide a Group for only one, or some, of the Select clauses, the others will be automatically used as dimensional values (analogous to the 'group bysemantics in SQL), within which the aggregations ofGroup` Cells will be performed.

You can join the same underlying Cell multiple times, but in that case you will need to use the 'as' function to distinguish them from each other.

The second parameter can be one of five predefined aggregates - 'count', 'sum', 'avg', 'min', and 'max' - or a custom function that produces your own aggregation of an array of Cell values.

The final three parameters, aggregateAdd, aggregateRemove, aggregateReplace need only be provided when you are using your own custom aggregate function. These give you the opportunity to reduce your custom function's algorithmic complexity by providing shortcuts that can nudge an aggregation result when a single value is added, removed, or replaced in the input values.

Examples

This example shows a query that calculates the average of all the values in a single selected Cell from a joined Table.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
    lowly: {species: 'worm'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
    worm: {price: 1},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join, group}) => {
  select('species', 'price');
  // from pets
  join('species', 'species');
  group('price', 'avg').as('avgPrice');
});

console.log(queries.getResultTable('query'));
// -> {0: {avgPrice: 3.75}}
// 2 dogs at 5, 1 cat at 4, 1 worm at 1: a total of 15 for 4 pets

This example shows a query that calculates the average of a two Cell values, aggregated by the two other dimensional 'group by' Cells.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', owner: 'alice'},
    felix: {species: 'cat', owner: 'bob'},
    cujo: {species: 'dog', owner: 'bob'},
    lowly: {species: 'worm', owner: 'alice'},
    carnaby: {species: 'parrot', owner: 'bob'},
    polly: {species: 'parrot', owner: 'alice'},
  })
  .setTable('species', {
    dog: {price: 5, legs: 4},
    cat: {price: 4, legs: 4},
    parrot: {price: 3, legs: 2},
    worm: {price: 1, legs: 0},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join, group}) => {
  select('pets', 'owner'); //    group by
  select('species', 'price'); // grouped
  // from pets
  join('species', 'species');
  group(
    'price',
    (cells) => Math.min(...cells.filter((cell) => cell > 2)),
    (current, add) => (add > 2 ? Math.min(current, add) : current),
    (current, remove) => (remove == current ? undefined : current),
    (current, add, remove) =>
      remove == current
        ? undefined
        : add > 2
          ? Math.min(current, add)
          : current,
  ).as('lowestPriceOver2');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {0: {owner: 'alice', lowestPriceOver2: 3}}
// -> {1: {owner: 'bob', lowestPriceOver2: 3}}
// Both have a parrot at 3. Alice's worm at 1 is excluded from aggregation.
Since

v2.0.0

GroupedAs

The GroupedAs type describes an object returned from calling a Group function so that the grouped Cell Id can be optionally aliased.

{as: (groupedCellId: Id) => void}
TypeDescription
as(groupedCellId: Id) => void

A function that lets you specify an alias for the grouped Cell Id.

Note that if two Group clauses are both aliased to the same name (or if you create two groups of the same underlying Cell, both without aliases), only the latter of two will be used in the query.

Example

This example shows a query that groups the same underlying Cell twice, for different purposes. Both groups are aliased with the 'as' function to disambiguate them.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', price: 5},
  felix: {species: 'cat', price: 4},
  cujo: {species: 'dog', price: 4},
  tom: {species: 'cat', price: 3},
});

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, group}) => {
  select('pets', 'species');
  select('pets', 'price');
  group('price', 'min').as('minPrice');
  group('price', 'max').as('maxPrice');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {0: {species: 'dog', minPrice: 4, maxPrice: 5}}
// -> {1: {species: 'cat', minPrice: 3, maxPrice: 4}}
Since

v2.0.0

Having

The Having type describes a function that lets you specify conditions to filter results, based on the grouped Cells resulting from a Group clause.

Calling this function with two parameters is used to include only those Rows for which a specified Cell in the query's root Table has a specified value.

(
  selectedOrGroupedCellId: string,
  equals: Cell,
): void
TypeDescription
selectedOrGroupedCellIdstring

The Id of the Cell in the query to test.

equalsCell

The value that the Cell has to have for the Row to be included in the result.

returnsvoid

This has no return value.

Since

v2.0.0


Calling this function with one callback parameter is used to include only those Rows which meet a calculated boolean condition.

(condition: (getSelectedOrGroupedCell: GetCell) => boolean): void
TypeDescription
condition(getSelectedOrGroupedCell: GetCell) => boolean

A callback that takes a GetCell function and that should return true for the Row to be included in the result.

returnsvoid

This has no return value.

Since

v2.0.0

The Having function is provided to the third query parameter of the setQueryDefinition method.

A Having condition has to be true for a Row to be included in the results. Each Having class is additive, as though combined with a logical 'and'. If you wish to create an 'or' expression, use the single parameter version of the type that allows arbitrary programmatic conditions.

The Where keyword differs from the Having keyword in that the former describes conditions that should be met by underlying Cell values (whether selected or not), and the latter describes conditions based on calculated and aggregated values - after Group clauses have been applied.

Whilst it is technically possible to use a Having clause even if the results have not been grouped with a Group clause, you should expect it to be less performant than using a Where clause, due to that being applied earlier in the query process.

Examples

This example shows a query that filters the results from a grouped Table by comparing a Cell from it with a value.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', price: 5},
  felix: {species: 'cat', price: 4},
  cujo: {species: 'dog', price: 4},
  tom: {species: 'cat', price: 3},
  carnaby: {species: 'parrot', price: 3},
  polly: {species: 'parrot', price: 3},
});

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, group, having}) => {
  select('pets', 'species');
  select('pets', 'price');
  group('price', 'min').as('minPrice');
  group('price', 'max').as('maxPrice');
  having('minPrice', 3);
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {0: {species: 'cat', minPrice: 3, maxPrice: 4}}
// -> {1: {species: 'parrot', minPrice: 3, maxPrice: 3}}

This example shows a query that filters the results from a grouped Table with a condition that is calculated from Cell values.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', price: 5},
  felix: {species: 'cat', price: 4},
  cujo: {species: 'dog', price: 4},
  tom: {species: 'cat', price: 3},
  carnaby: {species: 'parrot', price: 3},
  polly: {species: 'parrot', price: 3},
});

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, group, having}) => {
  select('pets', 'species');
  select('pets', 'price');
  group('price', 'min').as('minPrice');
  group('price', 'max').as('maxPrice');
  having(
    (getSelectedOrGroupedCell) =>
      getSelectedOrGroupedCell('minPrice') !=
      getSelectedOrGroupedCell('maxPrice'),
  );
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {0: {species: 'dog', minPrice: 4, maxPrice: 5}}
// -> {1: {species: 'cat', minPrice: 3, maxPrice: 4}}
// Parrots are filtered out because they have zero range in price.
Since

v2.0.0

Join

The Join type describes a function that lets you specify a Cell or calculated value to join the main query Table to other Tables, by their Row Id.

Calling this function with two Id parameters will indicate that the join to a Row in an adjacent Table is made by finding its Id in a Cell of the query's root Table.

(
  joinedTableId: string,
  on: string,
): JoinedAs
TypeDescription
joinedTableIdstring

The Id of the Table to join to.

onstring

The Id of the Cell in the root Table that contains the joined Table's Row Id.

returnsJoinedAs

A JoinedAs object so that the joined Table Id can be optionally aliased.

Since

v2.0.0


Calling this function with two parameters (where the second is a function) will indicate that the join to a Row in an adjacent Table is made by calculating its Id from the Cells and the Row Id of the query's root Table.

(
  joinedTableId: string,
  on: (getCell: GetCell, rowId: string) => undefined | string,
): JoinedAs
TypeDescription
joinedTableIdstring

The Id of the Table to join to.

on(getCell: GetCell, rowId: string) => undefined | string

A callback that takes a GetCell function and the root Table's Row Id. These can be used to programmatically calculate the joined Table's Row Id.

returnsJoinedAs

A JoinedAs object so that the joined Table Id can be optionally aliased.

Since

v2.0.0


Calling this function with three Id parameters will indicate that the join to a Row in distant Table is made by finding its Id in a Cell of an intermediately joined Table.

(
  joinedTableId: string,
  fromIntermediateJoinedTableId: string,
  on: string,
): JoinedAs
TypeDescription
joinedTableIdstring

The Id of the distant Table to join to.

fromIntermediateJoinedTableIdstring

The Id of an intermediate Table (which should have been in turn joined to the main query table via other Join clauses).

onstring

The Id of the Cell in the intermediate Table that contains the joined Table's Row Id.

returnsJoinedAs

A JoinedAs object so that the joined Table Id can be optionally aliased.

Since

v2.0.0


Calling this function with three parameters (where the third is a function) will indicate that the join to a Row in distant Table is made by calculating its Id from the Cells and the Row Id of an intermediately joined Table.

(
  joinedTableId: string,
  fromIntermediateJoinedTableId: string,
  on: (getIntermediateJoinedCell: GetCell, intermediateJoinedRowId: string) => undefined | string,
): JoinedAs
TypeDescription
joinedTableIdstring

The Id of the Table to join to.

fromIntermediateJoinedTableIdstring

The Id of an intermediate Table (which should have been in turn joined to the main query table via other Join clauses).

on(getIntermediateJoinedCell: GetCell, intermediateJoinedRowId: string) => undefined | string

A callback that takes a GetCell function and the intermediate Table's Row Id. These can be used to programmatically calculate the joined Table's Row Id.

returnsJoinedAs

A JoinedAs object so that the joined Table Id can be optionally aliased.

Since

v2.0.0

The Join function is provided to the third query parameter of the setQueryDefinition method.

You can join zero, one, or many Tables. You can join the same underlying Table multiple times, but in that case you will need to use the 'as' function to distinguish them from each other.

By default, each join is made from the main query Table to the joined table, but it is also possible to connect via an intermediate join Table to a more distant join Table.

Because a Join clause is used to identify which unique Row Id of the joined Table will be joined to each Row of the root Table, queries follow the 'left join' semantics you may be familiar with from SQL. This means that an unfiltered query will only ever return the same number of Rows as the main Table being queried, and indeed the resulting table (assuming it has not been aggregated) will even preserve the root Table's original Row Ids.

Examples

This example shows a query that joins a single Table by using an Id present in the main query Table.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', ownerId: '1'},
    felix: {species: 'cat', ownerId: '2'},
    cujo: {species: 'dog', ownerId: '3'},
  })
  .setTable('owners', {
    '1': {name: 'Alice'},
    '2': {name: 'Bob'},
    '3': {name: 'Carol'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
  select('species');
  select('owners', 'name');
  // from pets
  join('owners', 'ownerId');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {species: 'dog', name: 'Alice'}}
// -> {felix: {species: 'cat', name: 'Bob'}}
// -> {cujo: {species: 'dog', name: 'Carol'}}

This example shows a query that joins the same underlying Table twice, and aliases them (and the selected Cell Ids). Note the left-join semantics: Felix the cat was bought, but the seller was unknown. The record still exists in the ResultTable.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', buyerId: '1', sellerId: '2'},
    felix: {species: 'cat', buyerId: '2'},
    cujo: {species: 'dog', buyerId: '3', sellerId: '1'},
  })
  .setTable('humans', {
    '1': {name: 'Alice'},
    '2': {name: 'Bob'},
    '3': {name: 'Carol'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
  select('buyers', 'name').as('buyer');
  select('sellers', 'name').as('seller');
  // from pets
  join('humans', 'buyerId').as('buyers');
  join('humans', 'sellerId').as('sellers');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {buyer: 'Alice', seller: 'Bob'}}
// -> {felix: {buyer: 'Bob'}}
// -> {cujo: {buyer: 'Carol', seller: 'Alice'}}

This example shows a query that calculates the Id of the joined Table based from multiple values in the root Table rather than a single Cell.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  })
  .setTable('colorSpecies', {
    'brown-dog': {price: 6},
    'black-dog': {price: 5},
    'brown-cat': {price: 4},
    'black-cat': {price: 3},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
  select('colorSpecies', 'price');
  // from pets
  join(
    'colorSpecies',
    (getCell) => `${getCell('color')}-${getCell('species')}`,
  );
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {price: 6}}
// -> {felix: {price: 3}}
// -> {cujo: {price: 5}}

This example shows a query that joins two Tables, one through the intermediate other.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', ownerId: '1'},
    felix: {species: 'cat', ownerId: '2'},
    cujo: {species: 'dog', ownerId: '3'},
  })
  .setTable('owners', {
    '1': {name: 'Alice', state: 'CA'},
    '2': {name: 'Bob', state: 'CA'},
    '3': {name: 'Carol', state: 'WA'},
  })
  .setTable('states', {
    CA: {name: 'California'},
    WA: {name: 'Washington'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
  select(
    (getTableCell) =>
      `${getTableCell('species')} in ${getTableCell('states', 'name')}`,
  ).as('description');
  // from pets
  join('owners', 'ownerId');
  join('states', 'owners', 'state');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {description: 'dog in California'}}
// -> {felix: {description: 'cat in California'}}
// -> {cujo: {description: 'dog in Washington'}}
Since

v2.0.0

JoinedAs

The JoinedAs type describes an object returned from calling a Join function so that the joined Table Id can be optionally aliased.

{as: (joinedTableId: Id) => void}
TypeDescription
as(joinedTableId: Id) => void

A function that lets you specify an alias for the joined Table Id.

Note that if two Join clauses are both aliased to the same name (or if you create two joins to the same underlying Table, both without aliases), only the latter of two will be used in the query.

For the purposes of clarity, it's recommended to use an alias that does not collide with a real underlying Table (whether included in the query or not).

Example

This example shows a query that joins the same underlying Table twice, for different purposes. Both joins are aliased with the 'as' function to disambiguate them. Note that the selected Cells are also aliased.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', buyerId: '1', sellerId: '2'},
    felix: {species: 'cat', buyerId: '2'},
    cujo: {species: 'dog', buyerId: '3', sellerId: '1'},
  })
  .setTable('humans', {
    '1': {name: 'Alice'},
    '2': {name: 'Bob'},
    '3': {name: 'Carol'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
  select('buyers', 'name').as('buyer');
  select('sellers', 'name').as('seller');
  // from pets
  join('humans', 'buyerId').as('buyers');
  join('humans', 'sellerId').as('sellers');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {buyer: 'Alice', seller: 'Bob'}}
// -> {felix: {buyer: 'Bob'}}
// -> {cujo: {buyer: 'Carol', seller: 'Alice'}}
Since

v2.0.0

Select

The Select type describes a function that lets you specify a Cell or calculated value for including into the query's result.

Calling this function with one Id parameter will indicate that the query should select the value of the specified Cell from the query's root Table.

(cellId: string): SelectedAs
TypeDescription
cellIdstring

The Id of the Cell to fetch the value for.

returnsSelectedAs

A SelectedAs object so that the selected Cell Id can be optionally aliased.

Since

v2.0.0


Calling this function with two parameters will indicate that the query should select the value of the specified Cell from a Table that has been joined in the query.

(
  joinedTableId: string,
  joinedCellId: string,
): SelectedAs
TypeDescription
joinedTableIdstring

The Id of the Table to fetch the value from. If the underlying Table was joined 'as' a different Id, that should instead be used.

joinedCellIdstring

The Id of the Cell to fetch the value for.

returnsSelectedAs

A SelectedAs object so that the selected Cell Id can be optionally aliased.

Since

v2.0.0


Calling this function with one callback parameter will indicate that the query should select a calculated value, based on one or more Cell values in the root Table or a joined Table, or on the root Table's Row Id.

(getCell: (getTableCell: GetTableCell, rowId: string) => ResultCellOrUndefined): SelectedAs
TypeDescription
getCell(getTableCell: GetTableCell, rowId: string) => ResultCellOrUndefined

A callback that takes a GetTableCell function and the main Table's Row Id. These can be used to programmatically create a calculated value from multiple Cell values and the Row Id.

returnsSelectedAs

A SelectedAs object so that the selected Cell Id can be optionally aliased.

Since

v2.0.0

The Select function is provided to the third query parameter of the setQueryDefinition method. A query definition must call the Select function at least once, otherwise it will be meaningless and return no data.

Examples

This example shows a query that selects two Cells from the main query Table.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown', legs: 4},
  felix: {species: 'cat', color: 'black', legs: 4},
  cujo: {species: 'dog', color: 'black', legs: 4},
});

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select}) => {
  select('species');
  select('color');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {species: 'dog', color: 'brown'}}
// -> {felix: {species: 'cat', color: 'black'}}
// -> {cujo: {species: 'dog', color: 'black'}}

This example shows a query that selects two Cells, one from a joined Table.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', ownerId: '1'},
    felix: {species: 'cat', ownerId: '2'},
    cujo: {species: 'dog', ownerId: '3'},
  })
  .setTable('owners', {
    '1': {name: 'Alice'},
    '2': {name: 'Bob'},
    '3': {name: 'Carol'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
  select('species');
  select('owners', 'name');
  // from pets
  join('owners', 'ownerId');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {species: 'dog', name: 'Alice'}}
// -> {felix: {species: 'cat', name: 'Bob'}}
// -> {cujo: {species: 'dog', name: 'Carol'}}

This example shows a query that calculates a value from two underlying Cells.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', ownerId: '1'},
    felix: {species: 'cat', ownerId: '2'},
    cujo: {species: 'dog', ownerId: '3'},
  })
  .setTable('owners', {
    '1': {name: 'Alice'},
    '2': {name: 'Bob'},
    '3': {name: 'Carol'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
  select(
    (getTableCell) =>
      `${getTableCell('species')} for ${getTableCell('owners', 'name')}`,
  ).as('description');
  join('owners', 'ownerId');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {description: 'dog for Alice'}}
// -> {felix: {description: 'cat for Bob'}}
// -> {cujo: {description: 'dog for Carol'}}
Since

v2.0.0

SelectedAs

The SelectedAs type describes an object returned from calling a Select function so that the selected Cell Id can be optionally aliased.

{as: (selectedCellId: Id) => void}
TypeDescription
as(selectedCellId: Id) => void

A function that lets you specify an alias for the Cell Id.

If you are using a callback in the Select cause, it is highly recommended to use the 'as' function, since otherwise a machine-generated column name will be used.

Note that if two Select clauses are both aliased to the same name (or if two columns with the same underlying name are selected, both without aliases), only the latter of two will be used in the query.

Example

This example shows a query that selects two Cells, one from a joined Table. Both are aliased with the 'as' function:

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', ownerId: '1'},
    felix: {species: 'cat', ownerId: '2'},
    cujo: {species: 'dog', ownerId: '3'},
  })
  .setTable('owners', {
    '1': {name: 'Alice'},
    '2': {name: 'Bob'},
    '3': {name: 'Carol'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join}) => {
  select('species').as('petSpecies');
  select('owners', 'name').as('ownerName');
  // from pets
  join('owners', 'ownerId');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {petSpecies: 'dog', ownerName: 'Alice'}}
// -> {felix: {petSpecies: 'cat', ownerName: 'Bob'}}
// -> {cujo: {petSpecies: 'dog', ownerName: 'Carol'}}
Since

v2.0.0

Where

The Where type describes a function that lets you specify conditions to filter results, based on the underlying Cells of the root or joined Tables.

Calling this function with two parameters is used to include only those Rows for which a specified Cell in the query's root Table has a specified value.

(
  cellId: string,
  equals: Cell,
): void
TypeDescription
cellIdstring

The Id of the Cell in the query's root Table to test.

equalsCell

The value that the Cell has to have for the Row to be included in the result.

returnsvoid

This has no return value.

Since

v2.0.0


Calling this function with three parameters is used to include only those Rows for which a specified Cell in a joined Table has a specified value.

(
  joinedTableId: string,
  joinedCellId: string,
  equals: Cell,
): void
TypeDescription
joinedTableIdstring

The Id of the joined Table to test a value in. If the underlying Table was joined 'as' a different Id, that should instead be used.

joinedCellIdstring

The Id of the Cell in the joined Table to test.

equalsCell

The value that the Cell has to have for the Row to be included in the result.

returnsvoid

This has no return value.

Since

v2.0.0


Calling this function with one callback parameter is used to include only those Rows which meet a calculated boolean condition, based on values in the main and (optionally) joined Tables.

(condition: (getTableCell: GetTableCell) => boolean): void
TypeDescription
condition(getTableCell: GetTableCell) => boolean

A callback that takes a GetTableCell function and that should return true for the Row to be included in the result.

returnsvoid

This has no return value.

Since

v2.0.0

The Where function is provided to the third query parameter of the setQueryDefinition method.

If you do not specify a Where clause, you should expect every non-empty Row of the root Table to appear in the query's results.

A Where condition has to be true for a Row to be included in the results. Each Where class is additive, as though combined with a logical 'and'. If you wish to create an 'or' expression, use the single parameter version of the type that allows arbitrary programmatic conditions.

The Where keyword differs from the Having keyword in that the former describes conditions that should be met by underlying Cell values (whether selected or not), and the latter describes conditions based on calculated and aggregated values - after Group clauses have been applied.

Examples

This example shows a query that filters the results from a single Table by comparing an underlying Cell from it with a value.

import {createQueries, createStore} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, where}) => {
  select('species');
  where('species', 'dog');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {species: 'dog'}}
// -> {cujo: {species: 'dog'}}

This example shows a query that filters the results of a query by comparing an underlying Cell from a joined Table with a value. Note that the joined table has also been aliased, and so its alias is used in the Where clause.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', ownerId: '1'},
    felix: {species: 'cat', ownerId: '2'},
    cujo: {species: 'dog', ownerId: '3'},
  })
  .setTable('owners', {
    '1': {name: 'Alice', state: 'CA'},
    '2': {name: 'Bob', state: 'CA'},
    '3': {name: 'Carol', state: 'WA'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join, where}) => {
  select('species');
  // from pets
  join('owners', 'ownerId').as('petOwners');
  where('petOwners', 'state', 'CA');
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {fido: {species: 'dog'}}
// -> {felix: {species: 'cat'}}

This example shows a query that filters the results of a query with a condition that is calculated from underlying Cell values from the main and joined Table. Note that the joined table has also been aliased, and so its alias is used in the Where clause.

import {createQueries, createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', ownerId: '1'},
    felix: {species: 'cat', ownerId: '2'},
    cujo: {species: 'dog', ownerId: '3'},
  })
  .setTable('owners', {
    '1': {name: 'Alice', state: 'CA'},
    '2': {name: 'Bob', state: 'CA'},
    '3': {name: 'Carol', state: 'WA'},
  });

const queries = createQueries(store);
queries.setQueryDefinition('query', 'pets', ({select, join, where}) => {
  select('species');
  select('petOwners', 'state');
  // from pets
  join('owners', 'ownerId').as('petOwners');
  where(
    (getTableCell) =>
      getTableCell('pets', 'species') === 'cat' ||
      getTableCell('petOwners', 'state') === 'WA',
  );
});

queries.forEachResultRow('query', (rowId) => {
  console.log({[rowId]: queries.getResultRow('query', rowId)});
});
// -> {felix: {species: 'cat', state: 'CA'}}
// -> {cujo: {species: 'dog', state: 'WA'}}
Since

v2.0.0

Result type aliases

This is the collection of result type aliases within the queries module. There are 4 result type aliases in total.

ResultTable

The ResultTable type is the data structure representing the results of a query.

{[rowId: Id]: ResultRow}

A ResultTable is typically accessed with the getResultTable method or addResultTableListener method. It is similar to the Table type in the store module, but without schema-specific typing, and is a regular JavaScript object containing individual ResultRow objects, keyed by their Id.

Example
import type {ResultTable} from 'tinybase';

export const resultTable: ResultTable = {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat'},
};
Since

v2.0.0

ResultRow

The ResultRow type is the data structure representing a single row in the results of a query.

{[cellId: Id]: ResultCell}

A ResultRow is typically accessed with the getResultRow method or addResultRowListener method. It is similar to the Row type in the store module, but without schema-specific typing, and is a regular JavaScript object containing individual ResultCell objects, keyed by their Id.

Example
import type {ResultRow} from 'tinybase';

export const resultRow: ResultRow = {species: 'dog', color: 'brown'};
Since

v2.0.0

ResultCell

The ResultCell type is the data structure representing a single cell in the results of a query.

string | number | boolean

A ResultCell is typically accessed with the getResultCell method or addResultCellListener method. It is similar to the Cell type in the store module, but without schema-specific typing, and is a JavaScript string, number, or boolean.

Example
import type {ResultCell} from 'tinybase';

export const resultCell: ResultCell = 'dog';
Since

v2.0.0

ResultCellOrUndefined

The ResultCellOrUndefined type is the data structure representing a single cell in the results of a query, or the value undefined.

ResultCell | undefined
Since

v2.0.0

Listener type aliases

This is the collection of listener type aliases within the queries module. There are 11 listener type aliases in total.

ResultTableCellIdsListener

The ResultTableCellIdsListener type describes a function that is used to listen to changes to the Cell Ids that appear anywhere in a query's ResultTable.

(
  queries: Queries,
  tableId: Id,
  getIdChanges: GetIdChanges | undefined,
): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

tableIdId

The Id of the ResultTable that changed, which is also the query Id.

getIdChangesGetIdChanges | undefined
returnsvoid

This has no return value.

A ResultTableCellIdsListener is provided when using the addResultTableCellIdsListener method. See that method for specific examples.

When called, a ResultTableCellIdsListener is given a reference to the Queries object, and the Id of the ResultTable whose Cell Ids changed (which is the same as the query Id).

You can create new query definitions within the body of this listener, though obviously be aware of the possible cascading effects of doing so.

Since

v4.1.0

ResultTableListener

The ResultTableListener type describes a function that is used to listen to changes to a query's ResultTable.

(
  queries: Queries,
  tableId: Id,
  getCellChange: GetResultCellChange,
): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

tableIdId

The Id of the ResultTable that changed, which is also the query Id.

getCellChangeGetResultCellChange

A function that returns information about any ResultCell's changes.

returnsvoid

This has no return value.

A ResultTableListener is provided when using the addResultTableListener method. See that method for specific examples.

When called, a ResultTableListener is given a reference to the Queries object, the Id of the ResultTable that changed (which is the same as the query Id), and a GetResultCellChange function that can be used to query ResultCell values before and after the change.

You can create new query definitions within the body of this listener, though obviously be aware of the possible cascading effects of doing so.

Since

v2.0.0

ResultRowIdsListener

The ResultRowIdsListener type describes a function that is used to listen to changes to the ResultRow Ids in a query's ResultTable.

(
  queries: Queries,
  tableId: Id,
  getIdChanges: GetIdChanges | undefined,
): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

tableIdId

The Id of the ResultTable that changed, which is also the query Id.

getIdChangesGetIdChanges | undefined
returnsvoid

This has no return value.

A ResultRowIdsListener is provided when using the addResultRowIdsListener method. See that method for specific examples.

When called, a ResultRowIdsListener is given a reference to the Queries object, and the Id of the ResultTable whose ResultRow Ids changed (which is the same as the query Id).

You can create new query definitions within the body of this listener, though obviously be aware of the possible cascading effects of doing so.

Since

v2.0.0

ResultSortedRowIdsListener

The ResultSortedRowIdsListener type describes a function that is used to listen to changes to the sorted ResultRow Ids in a query's ResultTable.

(
  queries: Queries,
  tableId: Id,
  cellId: Id | undefined,
  descending: boolean,
  offset: number,
  limit: number | undefined,
  sortedRowIds: Ids,
): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

tableIdId

The Id of the ResultTable that changed, which is also the query Id.

cellIdId | undefined

The Id of the ResultCell whose values were used for the sorting.

descendingboolean

Whether the sorting was in descending order.

offsetnumber

The number of ResultRow Ids skipped.

limitnumber | undefined

The maximum number of ResultRow Ids returned.

sortedRowIdsIds

The sorted ResultRow Ids themselves.

returnsvoid

This has no return value.

A ResultSortedRowIdsListener is provided when using the addResultSortedRowIdsListener method. See that method for specific examples.

When called, a ResultSortedRowIdsListener is given a reference to the Queries object, the Id of the ResultTable whose ResultRow Ids changed (which is the same as the query Id), the ResultCell Id being used to sort them, whether descending or not, and the offset and limit of the number of Ids returned, for pagination purposes. It also receives the sorted array of Ids itself, so that you can use them in the listener without the additional cost of an explicit call to getResultSortedRowIds.

You can create new query definitions within the body of this listener, though obviously be aware of the possible cascading effects of doing so.

Since

v2.0.0

ResultRowCountListener

The ResultRowCountListener type describes a function that is used to listen to changes to the number of ResultRow objects in a query's ResultTable.

(
  queries: Queries,
  tableId: Id,
  count: number,
): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

tableIdId

The Id of the ResultTable that changed, which is also the query Id.

countnumber

The number of ResultRow objects in the ResultTable.

returnsvoid

This has no return value.

A ResultRowCountListener is provided when using the addResultRowCountListener method. See that method for specific examples.

When called, a ResultRowCountListener is given a reference to the Queries object, the Id of the ResultTable whose ResultRow Ids changed (which is the same as the query Id), and the count of ResultRow objects in the ResultTable.

You can create new query definitions within the body of this listener, though obviously be aware of the possible cascading effects of doing so.

Since

v4.1.0

ResultRowListener

The ResultRowListener type describes a function that is used to listen to changes to a ResultRow in a query's ResultTable.

(
  queries: Queries,
  tableId: Id,
  rowId: Id,
  getCellChange: GetResultCellChange,
): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

tableIdId

The Id of the ResultTable that changed, which is also the query Id.

rowIdId

The Id of the ResultRow that changed.

getCellChangeGetResultCellChange

A function that returns information about any ResultCell's changes.

returnsvoid

This has no return value.

A ResultRowListener is provided when using the addResultRowListener method. See that method for specific examples.

When called, a ResultRowListener is given a reference to the Queries object, the Id of the ResultTable that changed (which is the same as the query Id), the Id of the ResultRow that changed, and a GetResultCellChange function that can be used to query ResultCell values before and after the change.

You can create new query definitions within the body of this listener, though obviously be aware of the possible cascading effects of doing so.

Since

v2.0.0

ResultCellIdsListener

The ResultCellIdsListener type describes a function that is used to listen to changes to the ResultCell Ids in a ResultRow in a query's ResultTable.

(
  queries: Queries,
  tableId: Id,
  rowId: Id,
  getIdChanges: GetIdChanges | undefined,
): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

tableIdId

The Id of the ResultTable that changed, which is also the query Id.

rowIdId

The Id of the ResultRow that changed.

getIdChangesGetIdChanges | undefined
returnsvoid

This has no return value.

A ResultCellIdsListener is provided when using the addResultCellIdsListener method. See that method for specific examples.

When called, a ResultCellIdsListener is given a reference to the Queries object, the Id of the ResultTable that changed (which is the same as the query Id), and the Id of the ResultRow whose ResultCell Ids changed.

You can create new query definitions within the body of this listener, though obviously be aware of the possible cascading effects of doing so.

Since

v2.0.0

GetResultCellChange

The GetResultCellChange type describes a function that returns information about any ResultCell's changes during a transaction.

(
  tableId: Id,
  rowId: Id,
  cellId: Id,
): ResultCellChange
TypeDescription
tableIdId

The Id of the ResultTable to inspect.

rowIdId

The Id of the ResultRow to inspect.

cellIdId

The Id of the ResultCell to inspect.

returnsResultCellChange

A GetResultCellChange function is provided to every listener when called due the Store changing. The listener can then fetch the previous value of a ResultCell before the current transaction, the new value after it, and a convenience flag that indicates that the value has changed.

Since

v2.0.0

ResultCellChange

The ResultCellChange type describes a ResultCell's changes during a transaction.

[changed: boolean, oldCell: ResultCellOrUndefined, newCell: ResultCellOrUndefined]

This is returned by the GetResultCellChange function that is provided to every listener when called. This array contains the previous value of a ResultCell before the current transaction, the new value after it, and a convenience flag that indicates that the value has changed.

Since

v2.0.0

ResultCellListener

The ResultCellListener type describes a function that is used to listen to changes to a ResultCell in a query's ResultTable.

(
  queries: Queries,
  tableId: Id,
  rowId: Id,
  cellId: Id,
  newCell: ResultCell,
  oldCell: ResultCell,
  getCellChange: GetResultCellChange,
): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

tableIdId

The Id of the ResultTable that changed, which is also the query Id.

rowIdId

The Id of the ResultRow that changed.

cellIdId

The Id of the ResultCell that changed.

newCellResultCell

The new value of the ResultCell that changed.

oldCellResultCell

The old value of the ResultCell that changed.

getCellChangeGetResultCellChange

A function that returns information about any ResultCell's changes.

returnsvoid

This has no return value.

A ResultCellListener is provided when using the addResultCellListener method. See that method for specific examples.

When called, a ResultCellListener is given a reference to the Queries object, the Id of the ResultTable that changed (which is the same as the query Id), the Id of the ResultRow that changed, and the Id of ResultCell that changed. It is also given the new value of the ResultCell, the old value of the ResultCell, and a GetResultCellChange function that can be used to query ResultCell values before and after the change.

You can create new query definitions within the body of this listener, though obviously be aware of the possible cascading effects of doing so.

Since

v2.0.0

QueryIdsListener

The QueryIdsListener type describes a function that is used to listen to Query definitions being added or removed.

(queries: Queries): void
TypeDescription
queriesQueries

A reference to the Queries object that changed.

returnsvoid

This has no return value.

A QueryIdsListener is provided when using the addQueryIdsListener method. See that method for specific examples.

When called, a QueryIdsListener is given a reference to the Queries object.

Since

v2.0.0

Aggregators type aliases

This is the collection of aggregators type aliases within the queries module. There are 4 aggregators type aliases in total.

Aggregate

The Aggregate type describes a custom function that takes an array of Cell values and returns an aggregate of them.

(
  cells: Cell[],
  length: number,
): ResultCell
TypeDescription
cellsCell[]

The array of Cell values to be aggregated.

lengthnumber

The length of the array of Cell values to be aggregated.

returnsResultCell

There are a number of common predefined aggregators, such as for counting, summing, and averaging values. This type is instead used for when you wish to use a more complex aggregation of your own devising.

Since

v2.0.0

AggregateAdd

The AggregateAdd type describes a function that can be used to optimize a custom Aggregate by providing a shortcut for when a single value is added to the input values.

(
  current: Cell,
  add: Cell,
  length: number,
): ResultCellOrUndefined
TypeDescription
currentCell

The current value of the aggregation.

addCell

The Cell value being added to the aggregation.

lengthnumber

The length of the array of Cell values in the aggregation.

returnsResultCellOrUndefined

Some aggregation functions do not need to recalculate the aggregation of the whole set when one value changes. For example, when adding a new number to a series, the new sum of the series is the new value added to the previous sum.

If it is not possible to shortcut the aggregation based on just one value being added, return undefined and the aggregation will be completely recalculated.

When possible, if you are providing a custom Aggregate, seek an implementation of an AggregateAdd function that can reduce the complexity cost of growing the input data set.

Since

v2.0.0

AggregateRemove

The AggregateRemove type describes a function that can be used to optimize a custom Aggregate by providing a shortcut for when a single value is removed from the input values.

(
  current: Cell,
  remove: Cell,
  length: number,
): ResultCellOrUndefined
TypeDescription
currentCell

The current value of the aggregation.

removeCell

The Cell value being removed from the aggregation.

lengthnumber

The length of the array of Cell values in the aggregation.

returnsResultCellOrUndefined

Some aggregation functions do not need to recalculate the aggregation of the whole set when one value changes. For example, when removing a number from a series, the new sum of the series is the new value subtracted from the previous sum.

If it is not possible to shortcut the aggregation based on just one value being removed, return undefined and the aggregation will be completely recalculated. One example might be if you were taking the minimum of the values, and the previous minimum is being removed. The whole of the rest of the list will need to be re-scanned to find a new minimum.

When possible, if you are providing a custom Aggregate, seek an implementation of an AggregateRemove function that can reduce the complexity cost of shrinking the input data set.

Since

v2.0.0

AggregateReplace

The AggregateReplace type describes a function that can be used to optimize a custom Aggregate by providing a shortcut for when a single value in the input values is replaced with another.

(
  current: Cell,
  add: Cell,
  remove: Cell,
  length: number,
): ResultCellOrUndefined
TypeDescription
currentCell

The current value of the aggregation.

addCell

The Cell value being added to the aggregation.

removeCell

The Cell value being removed from the aggregation.

lengthnumber

The length of the array of Cell values in the aggregation.

returnsResultCellOrUndefined

Some aggregation functions do not need to recalculate the aggregation of the whole set when one value changes. For example, when replacing a number in a series, the new sum of the series is the previous sum, plus the new value, minus the old value.

If it is not possible to shortcut the aggregation based on just one value changing, return undefined and the aggregation will be completely recalculated.

When possible, if you are providing a custom Aggregate, seek an implementation of an AggregateReplace function that can reduce the complexity cost of changing the input data set in place.

Since

v2.0.0

Callback type aliases

This is the collection of callback type aliases within the queries module. There are 5 callback type aliases in total.

GetTableCell

The GetTableCell type describes a function that takes a Id and returns the Cell value for a particular Row, optionally in a joined Table.

When called with one parameter, this function will return the value of the specified Cell from the query's root Table for the Row being selected or filtered.

(cellId: string): CellOrUndefined
TypeDescription
cellIdstring

The Id of the Cell to fetch the value for.

returnsCellOrUndefined

A Cell value or undefined.

Since

v2.0.0


When called with two parameters, this function will return the value of the specified Cell from a Table that has been joined in the query, for the Row being selected or filtered.

(
  joinedTableId: string,
  joinedCellId: string,
): CellOrUndefined
TypeDescription
joinedTableIdstring

The Id of the Table to fetch the value from. If the underlying Table was joined 'as' a different Id, that should instead be used.

joinedCellIdstring

The Id of the Cell to fetch the value for.

returnsCellOrUndefined

A Cell value or undefined.

Since

v2.0.0

A GetTableCell can be provided when setting query definitions, specifically in the Select and Where clauses when you want to create or filter on calculated values. See those methods for specific examples.

Since

v2.0.0

ResultTableCallback

The ResultTableCallback type describes a function that takes a ResultTable's Id and a callback to loop over each ResultRow within it.

(
  tableId: Id,
  forEachRow: (rowCallback: ResultRowCallback) => void,
): void
TypeDescription
tableIdId

The Id of the ResultTable that the callback can operate on.

forEachRow(rowCallback: ResultRowCallback) => void

A function that will let you iterate over the ResultRow objects in this ResultTable.

returnsvoid

This has no return value.

A ResultTableCallback is provided when using the forEachResultTable method, so that you can do something based on every ResultTable in the Queries object. See that method for specific examples.

Since

v2.0.0

ResultRowCallback

The ResultRowCallback type describes a function that takes a ResultRow's Id and a callback to loop over each ResultCell within it.

(
  rowId: Id,
  forEachCell: (cellCallback: ResultCellCallback) => void,
): void
TypeDescription
rowIdId

The Id of the ResultRow that the callback can operate on.

forEachCell(cellCallback: ResultCellCallback) => void
returnsvoid

This has no return value.

A ResultRowCallback is provided when using the forEachResultRow method, so that you can do something based on every ResultRow in a ResultTable. See that method for specific examples.

Since

v2.0.0

ResultCellCallback

The ResultCellCallback type describes a function that takes a ResultCell's Id and its value.

(
  cellId: Id,
  cell: ResultCell,
): void
TypeDescription
cellIdId

The Id of the ResultCell that the callback can operate on.

cellResultCell

The value of the ResultCell.

returnsvoid

This has no return value.

A ResultCellCallback is provided when using the forEachResultCell method, so that you can do something based on every ResultCell in a ResultRow. See that method for specific examples.

Since

v2.0.0

QueryCallback

The QueryCallback type describes a function that takes a query's Id.

(queryId: Id): void
TypeDescription
queryIdId

The Id of the query that the callback can operate on.

returnsvoid

This has no return value.

A QueryCallback is provided when using the forEachQuery method, so that you can do something based on every query in the Queries object. See that method for specific examples.

Since

v2.0.0

Development type aliases

This is the collection of development type aliases within the queries module. There is only one type alias, QueriesListenerStats.

QueriesListenerStats

The QueriesListenerStats type describes the number of listeners registered with the Queries object, and can be used for debugging purposes.

{
  table: number;
  tableCellIds: number;
  rowCount: number;
  rowIds: number;
  sortedRowIds: number;
  row: number;
  cellIds: number;
  cell: number;
}
TypeDescription
tablenumber

The number of ResultTableListener functions registered with the Queries object.

tableCellIdsnumber

The number of ResultTableCellIdsListener functions registered with the Queries object, since v3.3.

rowCountnumber

The number of ResultRowCountListener functions registered with the Queries object, since v4.1.

rowIdsnumber

The number of ResultRowIdsListener functions registered with the Queries object.

sortedRowIdsnumber

The number of SortedRowIdsListener functions registered with the Queries object.

rownumber

The number of ResultRowListener functions registered with the Queries object.

cellIdsnumber

The number of ResultCellIdsListener functions registered with the Queries object.

cellnumber

The number of ResultCellListener functions registered with the Queries object.

A QueriesListenerStats object is returned from the getListenerStats method.

Since

v2.0.0

checkpoints

The checkpoints module of the TinyBase project provides the ability to create and track checkpoints made to the data in Store objects.

The main entry point to this module is the createCheckpoints function, which returns a new Checkpoints object. From there, you can create new checkpoints, go forwards or backwards to others, and register listeners for when the list of checkpoints change.

Since

v1.0.0

Interfaces

There is one interface, Checkpoints, within the checkpoints module.

Checkpoints

A Checkpoints object lets you set checkpoints on a Store, and move forward and backward through them to create undo and redo functionality.

Create a Checkpoints object easily with the createCheckpoints function. From there, you can set checkpoints (with the addCheckpoint method), query the checkpoints available (with the getCheckpointIds method), move forward and backward through them (with the goBackward method, goForward method, and goTo method), and add listeners for when the list checkpoints changes (with the addCheckpointIdsListener method).

Checkpoints work for both changes to tabular data and to keyed value data.

Every checkpoint can be given a label which can be used to describe the actions that changed the Store before this checkpoint. This can be useful for interfaces that let users 'Undo [last action]'.

Example

This example shows a simple lifecycle of a Checkpoints object: from creation, to adding a checkpoint, getting the list of available checkpoints, and then registering and removing a listener for them.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore()
  .setTables({pets: {fido: {sold: false}}})
  .setValue('open', true);

const checkpoints = createCheckpoints(store);
checkpoints.setSize(200);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

checkpoints.goBackward();
console.log(store.getCell('pets', 'fido', 'sold'));
// -> false
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', ['1']]

store.setValue('open', false);
checkpoints.addCheckpoint('closed');
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '2', []]

checkpoints.goBackward();
console.log(store.getValue('open'));
// -> true
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', ['2']]

const listenerId = checkpoints.addCheckpointIdsListener(() => {
  console.log(checkpoints.getCheckpointIds());
});
store.setCell('pets', 'fido', 'species', 'dog');
// -> [['0'], undefined, []]
checkpoints.addCheckpoint();
// -> [['0'], '3', []]
// Previous redo of checkpoints '1' and '2' are now not possible.

checkpoints.delListener(listenerId);
See also
Since

v1.0.0

Getter methods

This is the collection of getter methods within the Checkpoints interface. There are 4 getter methods in total.

getStore

The getStore method returns a reference to the underlying Store that is backing this Checkpoints object.

getStore(): Store
returnsStore

A reference to the Store.

Example

This example creates a Checkpoints object against a newly-created Store and then gets its reference in order to update its data and set a checkpoint.

import {createCheckpoints, createStore} from 'tinybase';

const checkpoints = createCheckpoints(createStore());
checkpoints.getStore().setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint();
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]
Since

v1.0.0

getCheckpoint

The getCheckpoint method fetches the label for a checkpoint, if it had been provided at the time of the addCheckpoint method or set subsequently with the setCheckpoint method.

getCheckpoint(checkpointId: string): undefined | string
TypeDescription
checkpointIdstring

The Id of the checkpoint to get the label for.

returnsundefined | string

A string label for the requested checkpoint, an empty string if it was never set, or undefined if the checkpoint does not exist.

If the checkpoint has had no label provided, this method will return an empty string.

Examples

This example creates a Store, adds a Checkpoints object, and sets a checkpoint with a label, before retrieving it again.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
store.setCell('pets', 'fido', 'sold', true);
console.log(checkpoints.addCheckpoint('sale'));
// -> '1'

console.log(checkpoints.getCheckpoint('1'));
// -> 'sale'

This example creates a Store, adds a Checkpoints object, and sets a checkpoint without a label, setting it subsequently. A non-existent checkpoint return an undefined label.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint();
console.log(checkpoints.getCheckpoint('1'));
// -> ''

checkpoints.setCheckpoint('1', 'sold');
console.log(checkpoints.getCheckpoint('1'));
// -> 'sold'

console.log(checkpoints.getCheckpoint('2'));
// -> undefined
Since

v1.0.0

getCheckpointIds

The getCheckpointIds method returns an array of the checkpoint Ids being managed by this Checkpoints object.

getCheckpointIds(): CheckpointIds
returnsCheckpointIds

A CheckpointIds array, containing the checkpoint Ids managed by this Checkpoints object.

The returned CheckpointIds array contains 'backward' checkpoint Ids, the current checkpoint Id (if present), and the 'forward' checkpointIds. Together, these are sufficient to understand the state of the Checkpoints object and what movement is possible backward or forward through the checkpoint stack.

Example

This example creates a Store, adds a Checkpoints object, and then gets the Ids of the checkpoints as it sets them and moves around the stack.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

checkpoints.goBackward();
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', ['1']]

checkpoints.goForward();
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]
Since

v1.0.0

hasCheckpoint

The hasCheckpoint method returns a boolean indicating whether a given Checkpoint exists in the Checkpoints object.

hasCheckpoint(checkpointId: string): boolean
TypeDescription
checkpointIdstring

The Id of a possible Checkpoint in the Checkpoints object.

returnsboolean

Whether a Checkpoint with that Id exists.

Example

This example shows two simple Checkpoint existence checks.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});
const checkpoints = createCheckpoints(store);
console.log(checkpoints.hasCheckpoint('0'));
// -> true
console.log(checkpoints.hasCheckpoint('1'));
// -> false
Since

v1.0.0

Setter methods

This is the collection of setter methods within the Checkpoints interface. There are only two setter methods, addCheckpoint and setCheckpoint.

addCheckpoint

The addCheckpoint method records a checkpoint of the Store into the Checkpoints object that can be reverted to in the future.

addCheckpoint(label?: string): string
TypeDescription
label?string

An optional label to describe the actions leading up to this checkpoint.

returnsstring

The Id of the newly-created checkpoint.

If no changes have been made to the Store since the last time a checkpoint was made, this method will have no effect.

The optional label parameter can be used to describe the actions that changed the Store before this checkpoint. This can be useful for interfaces that let users 'Undo [last action]'.

Example

This example creates a Store, adds a Checkpoints object, and adds two checkpoints, one with a label.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'species', 'dog');
const checkpointId1 = checkpoints.addCheckpoint();
console.log(checkpointId1);
// -> '1'

console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(checkpoints.getCheckpointIds());
// -> [['0', '1'], '2', []]

console.log(checkpoints.getCheckpoint('2'));
// -> 'sale'
Since

v1.0.0

setCheckpoint

The setCheckpoint method updates the label for a checkpoint in the Checkpoints object after it has been created.

setCheckpoint(
  checkpointId: string,
  label: string,
): Checkpoints
TypeDescription
checkpointIdstring

The Id of the checkpoint to set the label for.

labelstring

A label to describe the actions leading up to this checkpoint or left undefined if you want to clear the current label.

returnsCheckpoints

A reference to the Checkpoints object.

The label parameter can be used to describe the actions that changed the Store before the given checkpoint. This can be useful for interfaces that let users 'Undo [last action]'.

Generally you will provide the label parameter when the addCheckpoint method is called. Use this setCheckpoint method only when you need to change the label at a later point.

You cannot add a label to a checkpoint that does not yet exist.

Example

This example creates a Store, adds a Checkpoints object, and sets two checkpoints, one with a label, which are both then re-labelled.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint();
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');

console.log(checkpoints.getCheckpoint('1'));
// -> ''
console.log(checkpoints.getCheckpoint('2'));
// -> 'sale'

checkpoints.setCheckpoint('1', 'identified');
checkpoints.setCheckpoint('2', '');

console.log(checkpoints.getCheckpoint('1'));
// -> 'identified'
console.log(checkpoints.getCheckpoint('2'));
// -> ''

checkpoints.setCheckpoint('3', 'unknown');
console.log(checkpoints.getCheckpoint('3'));
// -> undefined
Since

v1.0.0

Listener methods

This is the collection of listener methods within the Checkpoints interface. There are only three listener methods, addCheckpointIdsListener, addCheckpointListener, and delListener.

addCheckpointIdsListener

The addCheckpointIdsListener method registers a listener function with the Checkpoints object that will be called whenever its set of checkpoints changes.

addCheckpointIdsListener(listener: CheckpointIdsListener): string
TypeDescription
listenerCheckpointIdsListener

The function that will be called whenever the checkpoints change.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a CheckpointIdsListener function, and will be called with a reference to the Checkpoints object.

Example

This example creates a Store, a Checkpoints object, and then registers a listener that responds to any changes to the checkpoints.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

const listenerId = checkpoints.addCheckpointIdsListener(() => {
  console.log('Checkpoint Ids changed');
  console.log(checkpoints.getCheckpointIds());
});

store.setCell('pets', 'fido', 'species', 'dog');
// -> 'Checkpoint Ids changed'
// -> [['0'], undefined, []]

checkpoints.addCheckpoint();
// -> 'Checkpoint Ids changed'
// -> [['0'], '1', []]

checkpoints.goBackward();
// -> 'Checkpoint Ids changed'
// -> [[], '0', ['1']]

checkpoints.goForward();
// -> 'Checkpoint Ids changed'
// -> [['0'], '1', []]

checkpoints.delListener(listenerId);
Since

v1.0.0

addCheckpointListener

The addCheckpointListener method registers a listener function with the Checkpoints object that will be called whenever the label of a checkpoint changes.

addCheckpointListener(
  checkpointId: IdOrNull,
  listener: CheckpointListener,
): string
TypeDescription
checkpointIdIdOrNull

The Id of the checkpoint to listen to, or null as a wildcard.

listenerCheckpointListener

The function that will be called whenever the checkpoint label changes.

returnsstring

A unique Id for the listener that can later be used to remove it.

You can either listen to a single checkpoint label (by specifying the checkpoint Id as the method's first parameter), or changes to any checkpoint label (by providing a null wildcard).

The provided listener is a CheckpointListener function, and will be called with a reference to the Checkpoints object, and the Id of the checkpoint whose label changed.

Example

This example creates a Store, a Checkpoints object, and then registers a listener that responds to any changes to a specific checkpoint label, including when the checkpoint no longer exists.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

const listenerId = checkpoints.addCheckpointListener('1', () => {
  console.log('Checkpoint 1 label changed');
  console.log(checkpoints.getCheckpoint('1'));
});

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
// -> 'Checkpoint 1 label changed'
// -> 'sale'

checkpoints.setCheckpoint('1', 'sold');
// -> 'Checkpoint 1 label changed'
// -> 'sold'

checkpoints.setCheckpoint('1', 'sold');
// The listener is not called when the label does not change.

checkpoints.goTo('0');
store.setCell('pets', 'fido', 'sold', false);
// -> 'Checkpoint 1 label changed'
// -> undefined
// The checkpoint no longer exists.

checkpoints.delListener(listenerId);
Since

v1.0.0

delListener

The delListener method removes a listener that was previously added to the Checkpoints object.

delListener(listenerId: string): Checkpoints
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsCheckpoints

A reference to the Checkpoints object.

Use the Id returned by the addCheckpointIdsListener method. Note that the Checkpoints object may re-use this Id for future listeners added to it.

Example

This example creates a Store, a Checkpoints object, registers a listener, and then removes it.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

const listenerId = checkpoints.addCheckpointIdsListener(() => {
  console.log('checkpoints changed');
});

store.setCell('pets', 'fido', 'species', 'dog');
// -> 'checkpoints changed'

checkpoints.addCheckpoint();
// -> 'checkpoints changed'

checkpoints.delListener(listenerId);

store.setCell('pets', 'fido', 'sold', 'true');
// -> undefined
// The listener is not called.
Since

v1.0.0

Configuration methods

This is the collection of configuration methods within the Checkpoints interface. There is only one method, setSize.

setSize

The setSize method lets you specify how many checkpoints the Checkpoints object will store.

setSize(size: number): Checkpoints
TypeDescription
sizenumber

The number of checkpoints that this Checkpoints object should hold.

returnsCheckpoints

A reference to the Checkpoints object.

If you set more checkpoints than this size, the oldest checkpoints will be pruned to make room for more recent ones.

The default size for a newly-created Checkpoints object is 100.

Example

This example creates a Store, adds a Checkpoints object, reduces the size of the Checkpoints object dramatically and then creates more than that number of checkpoints to demonstrate the oldest being pruned.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {views: 0}}});

const checkpoints = createCheckpoints(store);
checkpoints.setSize(2);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'views', 1);
checkpoints.addCheckpoint();
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

store.setCell('pets', 'fido', 'views', 2);
checkpoints.addCheckpoint();
console.log(checkpoints.getCheckpointIds());
// -> [['0', '1'], '2', []]

store.setCell('pets', 'fido', 'views', 3);
checkpoints.addCheckpoint();
console.log(checkpoints.getCheckpointIds());
// -> [['1', '2'], '3', []]
Since

v1.0.0

Iterator methods

This is the collection of iterator methods within the Checkpoints interface. There is only one method, forEachCheckpoint.

forEachCheckpoint

The forEachCheckpoint method takes a function that it will then call for each Checkpoint in a specified Checkpoints object.

forEachCheckpoint(checkpointCallback: CheckpointCallback): void
TypeDescription
checkpointCallbackCheckpointCallback

The function that should be called for every Checkpoint.

returnsvoid

This has no return value.

This method is useful for iterating over the structure of the Checkpoints object in a functional style. The checkpointCallback parameter is a CheckpointCallback function that will be called with the Id of each Checkpoint.

Example

This example iterates over each Checkpoint in a Checkpoints object.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});
const checkpoints = createCheckpoints(store);
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');

checkpoints.forEachCheckpoint((checkpointId, label) => {
  console.log(`${checkpointId}:${label}`);
});
// -> '0:'
// -> '1:sale'
Since

v1.0.0

Lifecycle methods

This is the collection of lifecycle methods within the Checkpoints interface. There are only three lifecycle methods, clear, clearForward, and destroy.

clear

The clear method resets this Checkpoints object to its initial state, removing all the checkpoints it has been managing.

clear(): Checkpoints
returnsCheckpoints

A reference to the Checkpoints object.

Obviously this method should be used with caution as it destroys the ability to undo or redo recent changes to the Store (though of course the Store itself is not reset by this method).

This method can be useful when a Store is being loaded via a Persister asynchronously after the Checkpoints object has been attached, and you don't want users to be able to undo the initial load of the data. In this case you could call the clear method immediately after the initial load so that that is the baseline from which all subsequent changes are tracked.

Example

This example creates a Store, a Checkpoints object, adds a listener, makes a change and then clears the checkpoints.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

const listenerId = checkpoints.addCheckpointIdsListener(() => {
  console.log('checkpoints changed');
});

store.setCell('pets', 'fido', 'color', 'brown');
// -> 'checkpoints changed'
checkpoints.addCheckpoint();
// -> 'checkpoints changed'
store.setCell('pets', 'fido', 'sold', true);
// -> 'checkpoints changed'
checkpoints.addCheckpoint();
// -> 'checkpoints changed'

console.log(store.getTables());
// -> {pets: {fido: {sold: true, color: 'brown'}}}
console.log(checkpoints.getCheckpointIds());
// -> [['0', '1'], '2', []]

checkpoints.clear();
// -> 'checkpoints changed'

console.log(store.getTables());
// -> {pets: {fido: {sold: true, color: 'brown'}}}
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

checkpoints.delListener(listenerId);
Since

v1.0.0

clearForward

The clearForward method resets just the 'redo' checkpoints it has been managing.

clearForward(): Checkpoints
returnsCheckpoints

A reference to the Checkpoints object.

Obviously this method should be used with caution as it destroys the ability to redo recent changes to the Store (though of course the Store itself is not reset by this method).

This method can be useful when you want to prohibit a user from redoing changes they have undone. The 'backward' redo stack, and current checkpoint are not affected.

Example

This example creates a Store, a Checkpoints object, adds a listener, makes a change and then clears the forward checkpoints.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

const listenerId = checkpoints.addCheckpointIdsListener(() => {
  console.log('checkpoints changed');
});

store.setCell('pets', 'fido', 'color', 'brown');
// -> 'checkpoints changed'
checkpoints.addCheckpoint();
// -> 'checkpoints changed'
store.setCell('pets', 'fido', 'sold', true);
// -> 'checkpoints changed'
checkpoints.addCheckpoint();
// -> 'checkpoints changed'
checkpoints.goBackward();
// -> 'checkpoints changed'

console.log(store.getTables());
// -> {pets: {fido: {color: 'brown', sold: false}}}
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', ['2']]

checkpoints.clearForward();
// -> 'checkpoints changed'

console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

checkpoints.delListener(listenerId);
Since

v4.5.3

destroy

The destroy method should be called when this Checkpoints object is no longer used.

destroy(): void

This guarantees that all of the listeners that the object registered with the underlying Store are removed and it can be correctly garbage collected.

Example

This example creates a Store, adds a Checkpoints object (that registers a CellListener with the underlying Store), and then destroys it again, removing the listener.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(store.getListenerStats().cell);
// -> 1

checkpoints.destroy();

console.log(store.getListenerStats().cell);
// -> 0
Since

v1.0.0

Movement methods

This is the collection of movement methods within the Checkpoints interface. There are only three movement methods, goBackward, goForward, and goTo.

goBackward

The goBackward method moves the state of the underlying Store back to the previous checkpoint, effectively performing an 'undo' on the Store data.

goBackward(): Checkpoints
returnsCheckpoints

A reference to the Checkpoints object.

If there is no previous checkpoint to return to, this method has no effect.

Example

This example creates a Store, a Checkpoints object, makes a change and then goes backward to the state of the Store before the change.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

checkpoints.goBackward();
console.log(store.getCell('pets', 'fido', 'sold'));
// -> false
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', ['1']]
Since

v1.0.0

goForward

The goForward method moves the state of the underlying Store forwards to a future checkpoint, effectively performing an 'redo' on the Store data.

goForward(): Checkpoints
returnsCheckpoints

A reference to the Checkpoints object.

If there is no future checkpoint to return to, this method has no effect.

Note that if you have previously used the goBackward method to undo changes, the forwards 'redo' stack will only exist while you do not make changes to the Store. In general the goForward method is expected to be used to redo changes that were just undone.

Examples

This example creates a Store, a Checkpoints object, makes a change and then goes backward to the state of the Store before the change. It then goes forward again to restore the state with the changes.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

checkpoints.goBackward();
console.log(store.getCell('pets', 'fido', 'sold'));
// -> false
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', ['1']]

checkpoints.goForward();
console.log(store.getCell('pets', 'fido', 'sold'));
// -> true
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

This example creates a Store, a Checkpoints object, makes a change and then goes backward to the state of the Store before the change. It makes a new change, the redo stack disappears, and then the attempt to forward again has no effect.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', []]

checkpoints.goBackward();
console.log(store.getCell('pets', 'fido', 'sold'));
// -> false
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', ['1']]

store.setCell('pets', 'fido', 'color', 'brown');
console.log(checkpoints.getCheckpointIds());
// -> [['0'], undefined, []]

checkpoints.goForward();
console.log(store.getCell('pets', 'fido', 'sold'));
// -> false
console.log(checkpoints.getCheckpointIds());
// -> [['0'], undefined, []]
// The original change cannot be redone.
Since

v1.0.0

goTo

The goTo method moves the state of the underlying Store backwards or forwards to a specified checkpoint.

goTo(checkpointId: string): Checkpoints
TypeDescription
checkpointIdstring

The Id of the checkpoint to move to.

returnsCheckpoints

A reference to the Checkpoints object.

If there is no checkpoint with the Id specified, this method has no effect.

Example

This example creates a Store, a Checkpoints object, makes two changes and then goes directly to the state of the Store before the two changes. It then goes forward again one change, also using the goTo method. Finally it tries to go to a checkpoint that does not exist.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {sold: false}}});

const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

store.setCell('pets', 'fido', 'color', 'brown');
checkpoints.addCheckpoint('identification');
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(checkpoints.getCheckpointIds());
// -> [['0', '1'], '2', []]

checkpoints.goTo('0');
console.log(store.getTables());
// -> {pets: {fido: {sold: false}}}
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', ['1', '2']]

checkpoints.goTo('1');
console.log(store.getTables());
// -> {pets: {fido: {sold: false, color: 'brown'}}}
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', ['2']]

checkpoints.goTo('3');
console.log(store.getTables());
// -> {pets: {fido: {sold: false, color: 'brown'}}}
console.log(checkpoints.getCheckpointIds());
// -> [['0'], '1', ['2']]
Since

v1.0.0

Development methods

This is the collection of development methods within the Checkpoints interface. There is only one method, getListenerStats.

getListenerStats

The getListenerStats method provides a set of statistics about the listeners registered with the Checkpoints object, and is used for debugging purposes.

getListenerStats(): CheckpointsListenerStats
returnsCheckpointsListenerStats

A CheckpointsListenerStats object containing Checkpoints listener statistics.

The CheckpointsListenerStats object contains a breakdown of the different types of listener.

The method is intended to be used during development to ensure your application is not leaking listener registrations, for example.

Example

This example gets the listener statistics of a Checkpoints object.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore();
const checkpoints = createCheckpoints(store);
checkpoints.addCheckpointIdsListener(() => {
  console.log('Checkpoint Ids changed');
});
checkpoints.addCheckpointListener(null, () => {
  console.log('Checkpoint label changed');
});

console.log(checkpoints.getListenerStats());
// -> {checkpointIds: 1, checkpoint: 1}
Since

v1.0.0

Functions

There is one function, createCheckpoints, within the checkpoints module.

createCheckpoints

The createCheckpoints function creates a Checkpoints object, and is the main entry point into the checkpoints module.

createCheckpoints(store: Store): Checkpoints
TypeDescription
storeStore

The Store for which to set Checkpoints.

returnsCheckpoints

A reference to the new Checkpoints object.

A given Store can only have one Checkpoints object associated with it. If you call this function twice on the same Store, your second call will return a reference to the Checkpoints object created by the first.

Examples

This example creates a Checkpoints object.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore();
const checkpoints = createCheckpoints(store);
console.log(checkpoints.getCheckpointIds());
// -> [[], '0', []]

This example creates a Checkpoints object, and calls the method a second time for the same Store to return the same object.

import {createCheckpoints, createStore} from 'tinybase';

const store = createStore();
const checkpoints1 = createCheckpoints(store);
const checkpoints2 = createCheckpoints(store);
console.log(checkpoints1 === checkpoints2);
// -> true
Since

v1.0.0

Type Aliases

These are the type aliases within the checkpoints module.

Listener type aliases

This is the collection of listener type aliases within the checkpoints module. There are only two listener type aliases, CheckpointIdsListener and CheckpointListener.

CheckpointIdsListener

The CheckpointIdsListener type describes a function that is used to listen to changes to the checkpoint Ids in a Checkpoints object.

(checkpoints: Checkpoints): void
TypeDescription
checkpointsCheckpoints

A reference to the Checkpoints object that changed.

returnsvoid

This has no return value.

A CheckpointIdsListener is provided when using the addCheckpointIdsListener method. See that method for specific examples.

When called, a CheckpointIdsListener is given a reference to the Checkpoints object.

Since

v1.0.0

CheckpointListener

The CheckpointListener type describes a function that is used to listen to changes to a checkpoint's label in a Checkpoints object.

(
  checkpoints: Checkpoints,
  checkpointId: Id,
): void
TypeDescription
checkpointsCheckpoints

A reference to the Checkpoints object that changed.

checkpointIdId

The Id of the checkpoint that changed.

returnsvoid

This has no return value.

A CheckpointListener is provided when using the addCheckpointListener method. See that method for specific examples.

When called, a CheckpointListener is given a reference to the Checkpoints object, and the Id of the checkpoint whose label changed.

Since

v1.0.0

Callback type aliases

This is the collection of callback type aliases within the checkpoints module. There is only one type alias, CheckpointCallback.

CheckpointCallback

The CheckpointCallback type describes a function that takes a Checkpoint's Id.

(
  checkpointId: Id,
  label?: string,
): void
TypeDescription
checkpointIdId

The Id of the Checkpoint that the callback can operate on.

label?string
returnsvoid

This has no return value.

A CheckpointCallback is provided when using the forEachCheckpoint method, so that you can do something based on every Checkpoint in the Checkpoints object. See that method for specific examples.

Since

v1.0.0

Identity type aliases

This is the collection of identity type aliases within the checkpoints module. There is only one type alias, CheckpointIds.

CheckpointIds

The CheckpointIds type is a representation of the list of checkpoint Ids stored in a Checkpoints object.

[Ids, Id | undefined, Ids]

There are three parts to a CheckpointsIds array:

  • The 'backward' checkpoint Ids that can be rolled backward to (in other words, the checkpoints in the undo stack for this Store). They are in chronological order with the oldest checkpoint at the start of the array.
  • The current checkpoint Id of the Store's state, or undefined if the current state has not been checkpointed.
  • The 'forward' checkpoint Ids that can be rolled forward to (in other words, the checkpoints in the redo stack for this Store). They are in chronological order with the newest checkpoint at the end of the array.
Since

v1.0.0

Development type aliases

This is the collection of development type aliases within the checkpoints module. There is only one type alias, CheckpointsListenerStats.

CheckpointsListenerStats

The CheckpointsListenerStats type describes the number of listeners registered with the Checkpoints object, and can be used for debugging purposes.

{
  checkpointIds: number;
  checkpoint: number;
}
TypeDescription
checkpointIdsnumber

The number of CheckpointIdsListener functions registered with the Checkpoints object.

checkpointnumber

The number of CheckpointListener functions registered with the Checkpoints object.

A CheckpointsListenerStats object is returned from the getListenerStats method.

Since

v1.0.0

common

The common module of the TinyBase project provides a small collection of common types used across other modules.

Since

v1.0.0

Functions

These are the functions within the common module.

defaultSorter

The defaultSorter function is provided as a convenience to sort keys alphanumerically, and can be provided to the sliceIdSorter and rowIdSorter parameters of the setIndexDefinition method in the indexes module, for example.

defaultSorter(
  sortKey1: SortKey,
  sortKey2: SortKey,
): number
TypeDescription
sortKey1SortKey

The first item of the pair to compare.

sortKey2SortKey

The second item of the pair to compare.

returnsnumber

A number indicating how to sort the pair.

Examples

This example creates an Indexes object.

import {createIndexes, createStore} from 'tinybase';

const store = createStore();
const indexes = createIndexes(store);
console.log(indexes.getIndexIds());
// -> []

This example creates a Store, creates an Indexes object, and defines an Index based on the first letter of the pets' names. The Slice Ids (and Row Ids within them) are alphabetically sorted using the defaultSorter function.

import {createIndexes, createStore, defaultSorter} from 'tinybase';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});

const indexes = createIndexes(store);
indexes.setIndexDefinition(
  'byFirst', //              indexId
  'pets', //                 tableId
  (_, rowId) => rowId[0], // each Row's Slice Id
  (_, rowId) => rowId, //    each Row's sort key
  defaultSorter, //          sort Slice Ids
  defaultSorter, //          sort Row Ids by sort key
);

console.log(indexes.getSliceIds('byFirst'));
// -> ['c', 'f']
console.log(indexes.getSliceRowIds('byFirst', 'f'));
// -> ['felix', 'fido']
Since

v1.0.0

getUniqueId

The getUniqueId function returns a unique string of a given length.

getUniqueId(length?: number): Id
TypeDescription
length?number

The desired length of the unique Id, defaulting to 16.

returnsId

A unique Id of the required length.

This is used internally by TinyBase for the synchronizer protocol and for unique MergeableStore identifiers. But it is useful enough for it to be publicly exposed for purposes such as identifying shared collaboration rooms, or creating other Ids that need to be unique.

The string may contain numbers, lower or upper case letters, or the '-' or '_' characters. This makes them URL-safe, and means they can be identified with a regex like /[-_0-9A-Za-z]+/.

This function prefers to use the crypto module to generate random numbers, but where that is not available (such as in React Native), a Math.random implementation is used. Whilst that may not be cryptographically sound, it should suffice for most TinyBase-related purposes.

Example

This example creates two 8 character long Ids and compares them.

import {getUniqueId} from 'tinybase';

const id1 = getUniqueId(8);
const id2 = getUniqueId(8);

console.log(id1.length);
// -> 8
console.log(id2.length);
// -> 8
console.log(id1 == id2);
// -> false
Since

v5.0.0

Type Aliases

These are the type aliases within the common module.

Callback type aliases

This is the collection of callback type aliases within the common module. There are only two callback type aliases, Callback and ParameterizedCallback.

Callback

The Callback type represents a function that is used as a callback and which does not take a parameter.

(): void
Since

v1.0.0

ParameterizedCallback

The ParameterizedCallback type represents a generic function that will take an optional parameter - such as the handler of a DOM event.

(parameter?: Parameter): void
TypeDescription
parameter?Parameter
returnsvoid

This has no return value.

Since

v1.0.0

General type aliases

This is the collection of general type aliases within the common module. There is only one type alias, Json.

Json

The Json type is a simple alias for a string, but is used to indicate that the string should be considered to be a JSON serialization of an object.

string
Since

v1.0.0

Identity type aliases

This is the collection of identity type aliases within the common module. There are only three identity type aliases, Id, IdOrNull, and Ids.

Id

The Id type is a simple alias for a string, but is used to indicate that the string should be considered to be the key of an object (such as a Row Id string used in a Table).

string
Since

v1.0.0

IdOrNull

The Id type is a simple alias for the union of a string or null value, where the string should be considered to be the key of an objects (such as a Row Id string used in a Table), and typically null indicates a wildcard - such as when used in the Store addRowListener method.

Id | null
Since

v1.0.0

Ids

The Ids type is a simple alias for an array of strings, but is used to indicate that the strings should be considered to be the keys of objects (such as the Row Id strings used in a Table).

Id[]
Since

v1.0.0

Parameter type aliases

This is the collection of parameter type aliases within the common module. There is only one type alias, SortKey.

SortKey

The SortKey type represents a value that can be used by a sort function.

string | number | boolean
Since

v1.0.0

persisters

The persisters module of the TinyBase project provides a simple framework for saving and loading Store and MergeableStore data, to and from different destinations, or underlying storage types.

Many entry points are provided (in separately installed modules), each of which returns different types of Persister that can load and save a Store. Between them, these allow you to store your TinyBase data locally, remotely, to SQLite and PostgreSQL databases, and across synchronization boundaries with CRDT frameworks.

PersisterStorageStoreMergeableStore
SessionPersisterBrowser session storageYesYes
LocalPersisterBrowser local storageYesYes
FilePersisterLocal file (where possible)YesYes
IndexedDbPersisterBrowser IndexedDBYesNo
RemotePersisterRemote serverYesNo
Sqlite3PersisterSQLite in Node, via sqlite3YesYes*
SqliteWasmPersisterSQLite in a browser, via sqlite-wasmYesYes*
ExpoSqlitePersisterSQLite in React Native, via expo-sqliteYesYes*
PostgresPersisterPostgreSQL, via postgresYesYes*
PglitePersisterPostgreSQL, via PGliteYesYes*
CrSqliteWasmPersisterSQLite CRDTs, via cr-sqlite-wasmYesNo
ElectricSqlPersisterElectric SQL, via electric-sqlYesNo
LibSqlPersisterLibSQL for Turso, via libsql-clientYesNo
PowerSyncPersisterPowerSync, via powersync-sdkYesNo
YjsPersisterYjs CRDTs, via yjsYesNo
AutomergePersisterAutomerge CRDTs, via automerge-repoYesNo
PartyKitPersisterPartyKit, via the persister-partykit-server moduleYesNo

(*) Note that SQLite- and PostgreSQL-based Persisters can currently only persist MergeableStore data when used with the JSON-based DpcJson mode, and not when using the DpcTabular mode.

Since persistence requirements can be different for every app, the createCustomPersister function in this module can also be used to easily create a fully customized way to save and load Store data.

Similarly, the createCustomSqlitePersister function and createCustomPostgreSqlPersister function can be used to build Persister objects against SQLite and PostgreSQL SDKs (or forks) that are not already included with TinyBase.

See also

Since

v1.0.0

Interfaces

There is one interface, Persister, within the persisters module.

Persister

A Persister object lets you save and load Store data to and from different locations, or underlying storage types.

This is useful for preserving Store or MergeableStore data between browser sessions or reloads, saving or loading browser state to or from a server, or saving Store data to disk in a environment with filesystem access.

Creating a Persister depends on the choice of underlying storage where the data is to be stored. Options include the createSessionPersister function, the createLocalPersister function, the createRemotePersister function, and the createFilePersister function, as just simple examples. The createCustomPersister function can also be used to easily create a fully customized way to save and load Store data.

Using the values of the Persists enum, the generic parameter to the Persister indicates whether it can handle a regular Store, a MergeableStore, or either. Consult the table in the overall persisters module documentation to see current support for each. The different levels of support are also described for each of the types of Persister themselves.

A Persister lets you explicit save or load data, with the save method and the load method respectively. These methods are both asynchronous (since the underlying data storage may also be) and return promises. As a result you should use the await keyword to call them in a way that guarantees subsequent execution order.

When you don't want to deal with explicit persistence operations, a Persister object also provides automatic saving and loading. Automatic saving listens for changes to the Store and persists the data immediately. Automatic loading listens (or polls) for changes to the persisted data and reflects those changes in the Store.

You can start automatic saving or loading with the startAutoSave method and startAutoLoad method. Both are asynchronous since they will do an immediate save and load before starting to listen for subsequent changes. You can stop the behavior with the stopAutoSave method and stopAutoLoad method (which are synchronous).

You may often want to have both automatic saving and loading of a Store so that changes are constantly synchronized (allowing basic state preservation between browser tabs, for example). The framework has some basic provisions to prevent race conditions - for example it will not attempt to save data if it is currently loading it and vice-versa - and will sequentially schedule methods that could cause race conditions.

That said, be aware that you should always comprehensively test your persistence strategy to understand the opportunity for data loss (in the case of trying to save data to a server under poor network conditions, for example).

To help debug such issues, since v4.0.4, the create methods for all Persister objects take an optional onIgnoredError argument. This is a handler for the errors that the Persister would otherwise ignore when trying to save or load data (such as when handling corrupted stored data). It's recommended you use this for debugging persistence issues, but only in a development environment. Database-based Persister objects also take an optional onSqlCommand argument for logging commands and queries made to the underlying database.

Examples

This example creates a Store, persists it to the browser's session storage as a JSON string, changes the persisted data, updates the Store from it, and finally destroys the Persister again.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load();
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();

This example creates a Store, and automatically saves and loads it to the browser's session storage as a JSON string. Changes to the Store data, or the persisted data (implicitly firing a StorageEvent), are reflected accordingly.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"felix":{"species":"cat"}}},{}]'

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Getter methods

This is the collection of getter methods within the Persister interface. There is only one method, getStore.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): PersistedStore<Persist>
returnsPersistedStore<Persist>

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

Listener methods

This is the collection of listener methods within the Persister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<Persist>): string
TypeDescription
listenerStatusListener<Persist>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the Persister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<Persister<Persist>>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<Persister<Persist>>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the Persister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<Persister<Persist>>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<Persister<Persist>>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<Persister<Persist>>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<Persister<Persist>>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the Persister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<Persister<Persist>>
returnsPromise<Persister<Persist>>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<Persister<Persist>>
returnsPromise<Persister<Persist>>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the Persister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Enumerations

These are the enumerations within the persisters module.

Lifecycle enumerations

This is the collection of lifecycle enumerations within the persisters module. There is only one enumeration, Status.

Status

The Status enum is used to indicate whether a Persister is idle, or loading or saving data.

{
  Idle: 0;
  Loading: 1;
  Saving: 2;
}
ValueDescription
Idle0

Indicates that the Persister is neither loading or saving data.

Loading1

Indicates that the Persister is loading data.

Saving2

Indicates that the Persister is saving data.

The enum is intended to be used to understand the status of the Persister in conjunction with the getStatus and addStatusListener methods.

Note that a Persister cannot be loading and saving data at the same time.

Since

v5.3.0

Mergeable enumerations

This is the collection of mergeable enumerations within the persisters module. There is only one enumeration, Persists.

Persists

The Persists enum is used to indicate whether a Persister can support a regular Store, a MergeableStore, or both.

{
  StoreOnly: 1;
  MergeableStoreOnly: 2;
  StoreOrMergeableStore: 3;
}
ValueDescription
StoreOnly1

Indicates that only a regular Store can be supported by a Persister.

MergeableStoreOnly2

Indicates that only a MergeableStore can be supported by a Persister.

StoreOrMergeableStore3

Indicates that either a regular Store or a MergeableStore can be supported by a Persister.

The enum is intended to be used by the author of a Persister to indicate which types of store can be persisted. If you discover type errors when trying to instantiate a Persister, it is most likely that you are passing in an unsupported type of store.

See the createCustomPersister method for an example of this enum being used.

Since

v5.0.0

Functions

These are the functions within the persisters module.

createCustomPersister

The createCustomPersister function creates a Persister object that you can configure to persist the Store in any way you wish.

createCustomPersister<ListenerHandle, Persist>(
  store: PersistedStore<Persist>,
  getPersisted: () => Promise<undefined | PersistedContent<Persist>>,
  setPersisted: (getContent: () => PersistedContent<Persist>, changes?: PersistedChanges<Persist>) => Promise<void>,
  addPersisterListener: (listener: PersisterListener<Persist>) => ListenerHandle | Promise<ListenerHandle>,
  delPersisterListener: (listenerHandle: ListenerHandle) => void,
  onIgnoredError?: (error: any) => void,
  persist?: Persist,
): Persister<Persist>
TypeDescription
storePersistedStore<Persist>

The Store to persist.

getPersisted() => Promise<undefined | PersistedContent<Persist>>

An asynchronous function which will fetch content from the persistence layer (or undefined if not present).

setPersisted(getContent: () => PersistedContent<Persist>, changes?: PersistedChanges<Persist>) => Promise<void>

An asynchronous function which will send content to the persistence layer. Since v4.0, it receives functions for getting the Store content and information about the changes made during a transaction. Since v5.0, the changes are received directly by reference, rather than an accessor.

addPersisterListener(listener: PersisterListener<Persist>) => ListenerHandle | Promise<ListenerHandle>

A function that will register a listener listener on underlying changes to the persistence layer. You can return a listening handle that will be provided again when delPersisterListener is called. This function can be asynchronous.

delPersisterListener(listenerHandle: ListenerHandle) => void

A function that will unregister the listener from the underlying changes to the persistence layer. It receives whatever was returned from your addPersisterListener implementation.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

persist?Persist

Since v5.0, an optional integer from the Persists enum to indicate which types of Store are supported by this Persister: 1 indicates only a regular Store is supported, 2 indicates only a MergeableStore is supported, and 3 indicates that both Store and MergeableStore are supported.

returnsPersister<Persist>

A reference to the new Persister object.

This is only used when developing custom Persisters, and most TinyBase users will not need to be particularly aware of it.

As well as providing a reference to the Store to persist, you must provide functions that handle how to fetch, write, and listen to, the persistence layer.

The other creation functions (such as the createSessionPersister function and createFilePersister function, for example) all use this function under the covers. See those implementations for ideas on how to implement your own Persister types.

This API changed in v4.0. Any custom persisters created on previous versions should be upgraded. Most notably, the setPersisted function parameter is provided with a getContent function to get the content from the Store itself, rather than being passed pre-serialized JSON. It also receives information about the changes made during a transaction. The getPersisted function must return the content (or nothing) rather than JSON. startListeningToPersisted has been renamed addPersisterListener, and stopListeningToPersisted has been renamed delPersisterListener.

Examples

This example creates a custom Persister object and persists a Store to a local string called persistedJson and which would automatically load by polling for changes every second. It implicitly supports only a regular Store.

import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
let persistedJson;

const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return JSON.parse(persistedJson);
  },
  async (getContent) => {
    // setPersisted
    persistedJson = JSON.stringify(getContent());
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);

await persister.save();
console.log(persistedJson);
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persistedJson = '[{"pets":{"fido":{"species":"dog","color":"brown"}}},{}]';
await persister.load();

console.log(store.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}

persister.destroy();

This example demonstrates a Persister creation function which returns a Persister. This can persists a store to a local string called persistedJson and which would automatically load by polling for changes every second. It emits warnings to the console and explicitly supports either a Store or a MergeableStore.

import {Persists, createCustomPersister} from 'tinybase/persisters';
import {createMergeableStore, createStore} from 'tinybase';

let persistedJson;
const createJsonPersister = (storeOrMergeableStore) =>
  createCustomPersister(
    storeOrMergeableStore,
    async () => {
      // getPersisted
      return JSON.parse(persistedJson);
    },
    async (getContent) => {
      // setPersisted
      persistedJson = JSON.stringify(getContent());
    },
    (listener) => setInterval(listener, 1000),
    (interval) => clearInterval(interval),
    console.warn,
    Persists.StoreOrMergeableStore,
  );

const store = createStore();
store.setTables({pets: {fido: {species: 'dog'}}});
const storePersister = createJsonPersister(store);
await storePersister.save();
console.log(persistedJson);
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'
storePersister.destroy();

const mergeableStore = createMergeableStore('mergeableStore1');
mergeableStore.setTables({pets: {fido: {species: 'dog'}}});
const mergeableStorePersister = createJsonPersister(mergeableStore);
await mergeableStorePersister.save();
console.log(JSON.parse(persistedJson));
// ->
[
  [
    {
      pets: [
        {
          fido: [
            {species: ['dog', 'Nn1JUF-----Zjl0M', 4176151067]},
            '',
            2722999044,
          ],
        },
        '',
        3367164653,
      ],
    },
    '',
    30627183,
  ],
  [{}, '', 0],
];
mergeableStorePersister.destroy();
Since

v1.0.0

createCustomPostgreSqlPersister

The createCustomSqlitePersister function creates a Persister object that you can configure to persist the Store to a PostgreSQL database.

createCustomPostgreSqlPersister<ListenerHandle, Persist>(
  store: PersistedStore<Persist>,
  configOrStoreTableName: undefined | string | DatabasePersisterConfig,
  executeCommand: DatabaseExecuteCommand,
  addChangeListener: (channel: string, listener: DatabaseChangeListener) => Promise<ListenerHandle>,
  delChangeListener: (listenerHandle: ListenerHandle) => void,
  onSqlCommand: undefined | (sql: string, params?: any[]) => void,
  onIgnoredError: undefined | (error: any) => void,
  destroy: () => void,
  persist: Persist,
  thing: any,
  getThing?: string,
): Persister<Persist>
TypeDescription
storePersistedStore<Persist>

The Store to persist.

configOrStoreTableNameundefined | string | DatabasePersisterConfig

A DatabasePersisterConfig object, or a string that will be used as the name of the Store's table in the database.

executeCommandDatabaseExecuteCommand

A function that will execute a command against the database.

addChangeListener(channel: string, listener: DatabaseChangeListener) => Promise<ListenerHandle>

A function that will register a listener for changes to the database.

delChangeListener(listenerHandle: ListenerHandle) => void

A function that will unregister the listener for changes to the database.

onSqlCommandundefined | (sql: string, params?: any[]) => void

A function that will be called for each SQL command executed against the database.

onIgnoredErrorundefined | (error: any) => void

A function that will be called for errors that are ignored by the Persister.

destroy() => void

A function that will be called to perform any extra clean up on the Persister.

persistPersist

An integer from the Persists enum to indicate which types of Store are supported by this Persister: 1 indicates only a regular Store is supported, 2 indicates only a MergeableStore is supported, and 3 indicates that both Store and MergeableStore are supported.

thingany

A reference to the database or connection that can be returned with a method, by default called getDb.

getThing?string

An optional string that will be used to get the reference to the database or connection from the Persister, defaulting to getDb.

returnsPersister<Persist>

A reference to the new Persister object.

This is only used when developing custom database-oriented Persisters, and most TinyBase users will not need to be particularly aware of it.

All of the TinyBase PostgreSQL-oriented Persister functions use this function under the covers, and so you may wish to look at those implementations for ideas on how to build your own Persister type, and as functional examples. Examine the implementation of the createPostgresPersister function as a good starting point, for example.

Since

v5.2.0

createCustomSqlitePersister

The createCustomSqlitePersister function creates a Persister object that you can configure to persist the Store to a SQLite database.

createCustomSqlitePersister<ListenerHandle, Persist>(
  store: PersistedStore<Persist>,
  configOrStoreTableName: undefined | string | DatabasePersisterConfig,
  executeCommand: DatabaseExecuteCommand,
  addChangeListener: (listener: DatabaseChangeListener) => ListenerHandle,
  delChangeListener: (listenerHandle: ListenerHandle) => void,
  onSqlCommand: undefined | (sql: string, params?: any[]) => void,
  onIgnoredError: undefined | (error: any) => void,
  destroy: () => void,
  persist: Persist,
  thing: any,
  getThing?: string,
): Persister<Persist>
TypeDescription
storePersistedStore<Persist>

The Store to persist.

configOrStoreTableNameundefined | string | DatabasePersisterConfig

A DatabasePersisterConfig object, or a string that will be used as the name of the Store's table in the database.

executeCommandDatabaseExecuteCommand

A function that will execute a command against the database.

addChangeListener(listener: DatabaseChangeListener) => ListenerHandle

A function that will register a listener for changes to the database.

delChangeListener(listenerHandle: ListenerHandle) => void

A function that will unregister the listener for changes to the database.

onSqlCommandundefined | (sql: string, params?: any[]) => void

A function that will be called for each SQL command executed against the database.

onIgnoredErrorundefined | (error: any) => void

A function that will be called for errors that are ignored by the Persister.

destroy() => void

A function that will be called to perform any extra clean up on the Persister.

persistPersist

An integer from the Persists enum to indicate which types of Store are supported by this Persister: 1 indicates only a regular Store is supported, 2 indicates only a MergeableStore is supported, and 3 indicates that both Store and MergeableStore are supported.

thingany

A reference to the database or connection that can be returned with a method, by default called getDb.

getThing?string

An optional string that will be used to get the reference to the database or connection from the Persister, defaulting to getDb.

returnsPersister<Persist>

A reference to the new SQLite-oriented Persister object.

This is only used when developing custom database-oriented Persisters, and most TinyBase users will not need to be particularly aware of it.

All of the TinyBase SQLite-oriented Persister functions use this function under the covers, and so you may wish to look at those implementations for ideas on how to build your own Persister type, and as functional examples. Examine the implementation of the createSqlite3Persister function as a good starting point, for example.

Since

v5.2.0

Type Aliases

These are the type aliases within the persisters module.

Listener type aliases

This is the collection of listener type aliases within the persisters module. There is only one type alias, StatusListener.

StatusListener

The StatusListener type describes a function that is used to listen to changes to the loading and saving status of the Persister.

(
  persister: Persister<Persist>,
  status: Status,
): void
TypeDescription
persisterPersister<Persist>

A reference to the Persister that changed.

statusStatus

The new loading or saving Status.

returnsvoid

This has no return value.

A StatusListener is provided when using the addStatusListener method. See that method for specific examples.

When called, a StatusListener is given a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Since

v5.3.0

Configuration type aliases

This is the collection of configuration type aliases within the persisters module. There are 6 configuration type aliases in total.

DatabasePersisterConfig

The DatabasePersisterConfig type describes the configuration of a database-oriented Persister, such as those for SQLite and PostgreSQL.

DpcJson | DpcTabular

There are two modes for persisting a Store with a database:

  • A JSON serialization of the whole Store, which is stored in a single row of a table (normally called tinybase) within the database. This is configured by providing a DpcJson object.
  • A tabular mapping of Table Ids to database table names (and vice-versa). Values are stored in a separate special table (normally called tinybase_values). This is configured by providing a DpcTabular object.

Please see the DpcJson and DpcTabular type documentation for more detail on each. If not specified otherwise, JSON serialization will be used for persistence.

Changes made to the database (outside of this Persister) are picked up immediately if they are made via the same connection or library that it is using. If the database is being changed by another client, the Persister needs to poll for changes. Hence both configuration types also contain an autoLoadIntervalSeconds property which indicates how often it should do that. This defaults to 1 second.

Note that all the nested types within this type have a 'Dpc' prefix, short for 'DatabasePersisterConfig'.

Examples

When applied to a database Persister, this DatabasePersisterConfig will load and save a JSON serialization from and to a table called my_tinybase, polling the database every 2 seconds. See DpcJson for more details on these settings.

import type {DatabasePersisterConfig} from 'tinybase';

export const databasePersisterConfig: DatabasePersisterConfig = {
  mode: 'json',
  storeTableName: 'my_tinybase',
  autoLoadIntervalSeconds: 2,
};

When applied to a database Persister, this DatabasePersisterConfig will load and save tabular data from and to tables specified in the load and save mappings. See DpcTabular for more details on these settings.

import type {DatabasePersisterConfig} from 'tinybase';

export const databasePersisterConfig: DatabasePersisterConfig = {
  mode: 'tabular',
  tables: {
    load: {petsInDb: 'pets', speciesInDb: 'species'},
    save: {pets: 'petsInDb', species: 'speciesInDb'},
  },
};
Since

v4.0.0

DpcJson

The DpcJson type describes the configuration of a database-oriented Persister operating in serialized JSON mode.

{
  mode: "json";
  storeTableName?: string;
  storeIdColumnName?: string;
  storeColumnName?: string;
  autoLoadIntervalSeconds?: number;
}
TypeDescription
mode"json"

The mode to be used for persisting the Store to the database, in this case JSON serialization. See the DpcTabular type for the alternative tabular mapping mode.

storeTableName?string

An optional string which indicates the name of a table in the database which will be used to serialize the Store content into. It defaults to tinybase.

storeIdColumnName?string

The optional name of the column in the database table that will be used as the Id for the Store, defaulting to '_id', since v5.0.

storeColumnName?string

The optional name of the column in the database table that will be used for the JSON of the Store, defaulting to 'store', since v5.0.

autoLoadIntervalSeconds?number

How often the Persister should poll the database for any changes made to it by other clients, defaulting to 1 second.

One setting is the storeTableName property, which indicates the name of a table in the database which will be used to serialize the Store content into. It defaults to tinybase.

That table in the database will be given two columns: a primary key column called _id, and one called store. (These column names can be changed using the rowIdColumnName and storeColumnName settings). The Persister will place a single row in this table with _ in the _id column, and the JSON serialization in the store column, something like the following.

> SELECT * FROM tinybase;
+-----+-----------------------------------------------------+
| _id | store                                               |
+-----+-----------------------------------------------------+
| _   | [{"pets":{"fido":{"species":"dog"}}},{"open":true}] |
+-----+-----------------------------------------------------+

The 'Dpc' prefix indicates that this type is used within the DatabasePersisterConfig type.

Example

When applied to a database Persister, this DatabasePersisterConfig will load and save a JSON serialization from and to a table called tinybase_json.

import type {DatabasePersisterConfig} from 'tinybase';

export const databasePersisterConfig: DatabasePersisterConfig = {
  mode: 'json',
  storeTableName: 'tinybase_json',
};
Since

v4.0.0

DpcTabular

The DpcTabular type describes the configuration of a database-oriented Persister that is operating in tabular mapping mode.

{
  mode: "tabular";
  tables?: {
    load?: DpcTabularLoad;
    save?: DpcTabularSave;
  };
  values?: DpcTabularValues;
  autoLoadIntervalSeconds?: number;
}
TypeDescription
mode"tabular"

The mode to be used for persisting the Store to the database, in this case tabular mapping. See the DpcJson type for the alternative JSON serialization mode.

tables?{ load?: DpcTabularLoad; save?: DpcTabularSave; }

The settings for how the Store Tables are mapped to and from the database.

values?DpcTabularValues

The settings for how the Store Values are mapped to and from the database.

autoLoadIntervalSeconds?number

How often the Persister should poll the database for any changes made to it by other clients, defaulting to 1 second.

This configuration can only be used when the Persister is persisting a regular Store. For those database-oriented Persister types that support MergeableStore data, you will need to use JSON-serialization, es described in the DpcJson section.

It is important to note that both the tabular mapping in ('save') and out ('load') of an underlying database are disabled by default. This is to ensure that if you pass in an existing populated database you don't run the immediate risk of corrupting or losing all your data.

This configuration therefore takes a tables property object (with child load and save property objects) and a values property object. These indicate how you want to load and save Tables and Values respectively. At least one of these two properties are required for the Persister to do anything!

Note that if you are planning to both load from and save to a database, it is important to make sure that the load and save table mappings are symmetrical. For example, consider the following.

import type {DatabasePersisterConfig} from 'tinybase';

export const databasePersisterConfig: DatabasePersisterConfig = {
  mode: 'tabular',
  tables: {
    load: {petsInDb: 'pets', speciesInDb: 'species'},
    save: {pets: 'petsInDb', species: 'speciesInDb'},
  },
};

See the documentation for the DpcTabularLoad, DpcTabularSave, and DpcTabularValues types for more details on how to configure the tabular mapping mode.

Columns in SQLite database have no type, and so in this mode, the table can contain strings and numbers for Cells and Values, just as TinyBase does. Booleans, unfortunately, are stored as 0 or 1 in SQLite, and cannot be distinguished from numbers.

In PostgreSQL databases, all Cell and Value columns are expected to be typed as text, and the strings, booleans, and numbers are all JSON-encoded by the Persister.

The 'Dpc' prefix indicates that this type is used within the DatabasePersisterConfig type.

Example

When applied to a database Persister, this DatabasePersisterConfig will load and save Tables data from and to tables specified in the load and save mappings, and Values data from and to a table called my_tinybase_values.

import type {DatabasePersisterConfig} from 'tinybase';

export const databasePersisterConfig: DatabasePersisterConfig = {
  mode: 'tabular',
  tables: {
    load: {petsInDb: 'pets', speciesInDb: 'species'},
    save: {pets: 'petsInDb', species: 'speciesInDb'},
  },
  values: {
    load: true,
    save: true,
    tableName: 'my_tinybase_values',
  },
};
Since

v4.0.0

DpcTabularLoad

The DpcTabularLoad type describes the configuration for loading Tables in a database-oriented Persister that is operating in tabular mode.

{[tableName: string]: {
  tableId: Id;
  rowIdColumnName?: string;
} | Id}

It is an object where each key is a name of a database table, and the value is a child configuration object for how that table should be loaded into the Store. The properties of the child configuration object are:

TypeDescription
tableIdIdThe Id of the Store Table into which data from this database table should be loaded.
rowIdColumnName?stringThe optional name of the column in the database table that will be used as the Row Ids in the Store Table, defaulting to '_id'.

As a shortcut, if you do not need to specify a custom rowIdColumnName, you can simply provide the Id of the Store Table instead of the whole object.

The 'Dpc' prefix indicates that this type is used within the DatabasePersisterConfig type.

Example

When applied to a database Persister, this DatabasePersisterConfig will load the data of two database tables (called 'petsInDb' and 'speciesInDb') into two Store Tables (called 'pets' and 'species'). One has a column for the Row Id called 'id' and the other defaults it to '_id'.

import type {DatabasePersisterConfig} from 'tinybase';

export const databasePersisterConfig: DatabasePersisterConfig = {
  mode: 'tabular',
  tables: {
    load: {
      petsInDb: {tableId: 'pets', rowIdColumnName: 'id'},
      speciesInDb: 'species',
    },
  },
};

Imagine database tables that look like this:

> SELECT * FROM petsInDb;
+-------+---------+-------+
| id    | species | color |
+-------+---------+-------+
| fido  | dog     | brown |
| felix | cat     | black |
+-------+---------+-------+

> SELECT * FROM speciesInDb;
+------+-------+
| _id  | price |
+------+-------+
| dog  | 5     |
| cat  | 4     |
+------+-------+

With the configuration above, this will load into a Store with Tables that look like this:

{
  "pets": {
    "fido": {"species": "dog", "color": "brown"},
    "felix": {"species": "cat", "color": "black"},
  },
  "species": {
    "dog": {"price": 5},
    "cat": {"price": 4},
  },
}

The example above represents what happens with a SQLite Persister. In PostgreSQL databases, all Cell and Value columns are expected to be typed as text, and the strings, booleans, and numbers would be JSON-encoded if you queried them.

Since

v4.0.0

DpcTabularSave

The DpcTabularSave type describes the configuration for saving Tables in a database-oriented Persister that is operating in tabular mode.

{[tableId: Id]: {
  tableName: string;
  rowIdColumnName?: string;
  deleteEmptyColumns?: boolean;
  deleteEmptyTable?: boolean;
} | string}

It is an object where each key is an Id of a Store Table, and the value is a child configuration object for how that Table should be saved out to the database. The properties of the child configuration object are:

TypeDescription
tableNamestringThe name of the database table out to which the Store Table should be saved.
rowIdColumnName?stringThe optional name of the column in the database table that will be used to save the Row Ids from the Store Table, defaulting to '_id'.
deleteEmptyColumns?booleanWhether columns in the database table will be removed if they are empty in the Store Table, defaulting to false.
deleteEmptyTable?booleanWhether tables in the database will be removed if the Store Table is empty, defaulting to false.

As a shortcut, if you do not need to specify a custom rowIdColumnName, or enable the deleteEmptyColumns or deleteEmptyTable settings, you can simply provide the name of the database table instead of the whole object.

deleteEmptyColumns and deleteEmptyTable only have a guaranteed effect when an explicit call is made to the Persister's save method. Columns and tables will not necessarily be removed when the Persister is incrementally 'autoSaving', due to performance reasons. If you want to be sure that your database table matches a TinyBase Table without any extraneous columns, simply call the save method at an idle moment.

The 'Dpc' prefix indicates that this type is used within the DatabasePersisterConfig type.

Example

When applied to a database Persister, this DatabasePersisterConfig will save the data of two Store Tables (called 'pets' and 'species') into two database tables (called 'petsInDb' and 'speciesInDb'). One has a column for the Row Id called 'id' and will delete columns and the whole table if empty, the other defaults to '_id' and will not delete columns or the whole table if empty.

import type {DatabasePersisterConfig} from 'tinybase';

export const databasePersisterConfig: DatabasePersisterConfig = {
  mode: 'tabular',
  tables: {
    save: {
      pets: {
        tableName: 'petsInDb',
        deleteEmptyColumns: true,
        deleteEmptyTable: true,
      },
      species: 'speciesInDb',
    },
  },
};

Imagine a Store with Tables that look like this:

{
  "pets": {
    "fido": {"species": "dog", "color": "brown"},
    "felix": {"species": "cat", "color": "black"},
  },
  "species": {
    "dog": {"price": 5},
    "cat": {"price": 4},
  },
}

With the configuration above, this will save out to a database with tables that look like this:

> SELECT * FROM petsInDb;
+-------+---------+-------+
| id    | species | color |
+-------+---------+-------+
| fido  | dog     | brown |
| felix | cat     | black |
+-------+---------+-------+

> SELECT * FROM speciesInDb;
+------+-------+
| _id  | price |
+------+-------+
| dog  | 5     |
| cat  | 4     |
+------+-------+

The example above represents what happens with a SQLite Persister. In PostgreSQL databases, all Cell and Value columns are expected to be typed as text, and the strings, booleans, and numbers would be JSON-encoded if you queried them.

Since

v4.0.0

DpcTabularValues

The DpcTabularValues type describes the configuration for handling Values in a database-oriented Persister that is operating in tabular mode.

{
  load?: boolean;
  save?: boolean;
  tableName?: string;
}
TypeDescription
load?boolean

Whether Store Values will be loaded from a database table.

save?boolean

Whether Store Values will be saved to a database table.

tableName?string

The optional name of the database table from and to which the Store Values should be loaded or saved, defaulting to tinybase_values.

Note that both loading and saving of Values from and to the database are disabled by default.

The 'Dpc' prefix indicates that this type is used within the DatabasePersisterConfig type.

Example

When applied to a database Persister, this DatabasePersisterConfig will load and save the data of a Store's Values into a database table called 'my_tinybase_values'.

import type {DatabasePersisterConfig} from 'tinybase';

export const databasePersisterConfig: DatabasePersisterConfig = {
  mode: 'tabular',
  values: {
    load: true,
    save: true,
    tableName: 'my_tinybase_values',
  },
};
Since

v4.0.0

Creation type aliases

This is the collection of creation type aliases within the persisters module. There are only three creation type aliases, DatabaseChangeListener, DatabaseExecuteCommand, and PersisterListener.

DatabaseChangeListener

The DatabaseChangeListener type describes a function that is used to listen for changes to the data in a database.

(tableName: string): void
TypeDescription
tableNamestring

The name of the table that has changed.

returnsvoid

This has no return value.

This is only used when developing custom database-oriented Persisters, and most TinyBase users will not need to be particularly aware of it.

This function should be called with the name of a relevant table that has changed, possible through the use of events, triggers, or notifications, dependent on the specific database implementation.

Since

v5.2.0

DatabaseExecuteCommand

The DatabaseExecuteCommand type describes a function that is used to execute commands against a database.

(
  sql: string,
  params?: any[],
): Promise<{[field: string]: any}[]>
TypeDescription
sqlstring

The SQL string to execute, which may include positional parameter placeholders.

params?any[]

An array of parameters to pass to the SQL command.

returnsPromise<{[field: string]: any}[]>

This is only used when developing custom database-oriented Persisters, and most TinyBase users will not need to be particularly aware of it.

It is modelled around the common pattern of database SDKs being able to execute commands with parameters, and have those (probably asynchronous) command executions return an array of objects, where each object represents a row.

Since

v5.2.0

PersisterListener

A PersisterListener is a generic representation of the callback that lets a Persister inform the store that a change has happened to the underlying data.

(
  content?: PersistedContent<Persist>,
  changes?: PersistedChanges<Persist>,
): void
TypeDescription
content?PersistedContent<Persist>

If provided, this is a Content object from the the Persister that will be used to immediately wholesale update the Store.

changes?PersistedChanges<Persist>

If provided, this is a Changes object from the the Persister that will be used to immediately incrementally update the Store. This takes priority over the content argument above if present.

returnsvoid

This has no return value.

Using the values of the Persists enum, the generic parameter indicates whether the Persister is handling content and changes from a regular Store, a MergeableStore, or either.

If the listener is called with the changes parameter, it will be used to make an incremental change to the Store. If not, but the content parameter is available, that will be used to make a wholesale change to the Store. If neither are present, the content will be loaded using the Persister's load method. Prior to v5.0, these parameters were callbacks and the overall type was non-generic.

Since

v4.0.0

Mergeable type aliases

This is the collection of mergeable type aliases within the persisters module. There are 4 mergeable type aliases in total.

PersistedStore

The PersistedStore type is a generic representation of the type of store being handled by a Persister.

Persist extends Persists.StoreOrMergeableStore ? Store | MergeableStore : Persist extends Persists.MergeableStoreOnly ? MergeableStore : Store

Using the values of the Persists enum, the generic parameter indicates whether the Persister is handling a regular Store, a MergeableStore, or either.

If the generic parameter is unspecified, the StoreOnly enum value is used, meaning that PersistedStore is equivalent to a regular Store.

Since

v5.0.0

AnyPersister

The AnyPersister type is a convenient alias for any type of Persister that can persist Store or MergeableStore objects.

Persister<Persists>
Since

v5.3.0

PersistedChanges

The PersistedChanges type is a generic representation of changes made to the type of store being handled by a Persister.

Persist extends Persists.StoreOrMergeableStore ? Changes | MergeableChanges : Persist extends Persists.MergeableStoreOnly ? MergeableChanges : Changes

Using the values of the Persists enum, the generic parameter indicates whether the Persister is handling changes for a regular Store (the Changes type), a MergeableStore (the MergeableChanges type), or either (the union of the two).

Since

v5.0.0

PersistedContent

The PersistedContent type is a generic representation of the content in the type of store being handled by a Persister.

Persist extends Persists.StoreOrMergeableStore ? Content | MergeableContent : Persist extends Persists.MergeableStoreOnly ? MergeableContent : Content

Using the values of the Persists enum, the generic parameter indicates whether the Persister is handling content from a regular Store (the Content type), a MergeableStore (the MergeableContent type), or either (the union of the two).

If the generic parameter is unspecified, the StoreOnly enum value is used, meaning that PersistedContent is equivalent to the Content type.

Since

v5.0.0

Development type aliases

This is the collection of development type aliases within the persisters module. There is only one type alias, PersisterStats.

PersisterStats

The PersisterStats type describes the number of times a Persister object has loaded or saved data.

{
  loads: number;
  saves: number;
}
TypeDescription
loadsnumber

The number of times data has been loaded.

savesnumber

The number of times data has been saved.

A PersisterStats object is returned from the getStats method.

Since

v1.0.0

persister-automerge

The persister-automerge module of the TinyBase project provides a way to save and load Store data to and from an Automerge document.

A single entry point, the createAutomergePersister function, is provided, which returns a new Persister object that can bind a Store to a provided Automerge document handle (and in turn, its document).

See also

Third-Party CRDT Persistence guide

Since

v4.0.0

Interfaces

There is one interface, AutomergePersister, within the persister-automerge module.

AutomergePersister

The AutomergePersister interface represents a Persister that lets you save and load Store data to and from an Automerge document.

You should use the createAutomergePersister function to create an AutomergePersister object.

It is a minor extension to the Persister interface and simply provides an extra getDocHandle method for accessing the Automerge document handler the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the AutomergePersister interface. There are only two getter methods, getStore and getDocHandle.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getDocHandle

The getDocHandle method returns the Automerge document handler the Store is being persisted to.

getDocHandle(): DocHandle<any>
returnsDocHandle<any>

The Automerge document handler.

Example

This example creates a Persister object against a newly-created Store and then gets the Automerge document handler back out again.

import {Repo} from '@automerge/automerge-repo';
import {createAutomergePersister} from 'tinybase/persisters/persister-automerge';
import {createStore} from 'tinybase';

const docHandler = new Repo({network: []}).create();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createAutomergePersister(store, docHandler);

console.log(persister.getDocHandle() == docHandler);
// -> true

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the AutomergePersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the AutomergePersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<AutomergePersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<AutomergePersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the AutomergePersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<AutomergePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<AutomergePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<AutomergePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<AutomergePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the AutomergePersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<AutomergePersister>
returnsPromise<AutomergePersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<AutomergePersister>
returnsPromise<AutomergePersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the AutomergePersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createAutomergePersister, within the persister-automerge module.

createAutomergePersister

The createAutomergePersister function creates an AutomergePersister object that can persist the Store to an Automerge document.

createAutomergePersister(
  store: Store,
  docHandle: DocHandle<any>,
  docMapName?: string,
  onIgnoredError?: (error: any) => void,
): AutomergePersister
TypeDescription
storeStore

The Store to persist.

docHandleDocHandle<any>

The Automerge document handler to persist the Store with.

docMapName?string

The name of the map used inside the Automerge document to sync the Store to (which otherwise will default to 'tinybase').

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsAutomergePersister

A reference to the new AutomergePersister object.

An AutomergePersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide the Automerge document handler to persist it with.

Examples

This example creates a AutomergePersister object and persists the Store to an Automerge document.

import {Repo} from '@automerge/automerge-repo';
import {createAutomergePersister} from 'tinybase/persisters/persister-automerge';
import {createStore} from 'tinybase';

const docHandler = new Repo({network: []}).create();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createAutomergePersister(store, docHandler);

await persister.save();
// Store will be saved to the document.

console.log(await docHandler.doc());
// -> {tinybase: {t: {pets: {fido: {species: 'dog'}}}, v: {}}}

persister.destroy();

This more complex example uses Automerge networking to keep two Store objects (each with their own Persister objects and Automerge documents) in sync with each other using a network.

import {BroadcastChannelNetworkAdapter} from '@automerge/automerge-repo-network-broadcastchannel';
import {Repo} from '@automerge/automerge-repo';
import {createAutomergePersister} from 'tinybase/persisters/persister-automerge';
import {createStore} from 'tinybase';

// Bind the first Store to a network-enabled automerge-repo
const repo1 = new Repo({
  network: [new BroadcastChannelNetworkAdapter()],
});
const docHandler1 = repo1.create();
await docHandler1.doc();
const store1 = createStore();
const persister1 = createAutomergePersister(store1, docHandler1);
await persister1.startAutoLoad();
await persister1.startAutoSave();

// Bind the second Store to a different network-enabled automerge-repo
const repo2 = new Repo({
  network: [new BroadcastChannelNetworkAdapter()],
});
const docHandler2 = repo2.find(docHandler1.documentId);
await docHandler2.doc();
const store2 = createStore();
const persister2 = createAutomergePersister(store2, docHandler2);
await persister2.startAutoLoad();
await persister2.startAutoSave();

// A function that waits briefly and then for the documents to synchronize
// with each other, merely for the purposes of sequentiality in this example.
const syncDocsWait = async () => {
  await new Promise((resolve) => setTimeout(() => resolve(0), 100));
  await docHandler1.doc();
  await docHandler2.doc();
};

// Wait for the documents to synchronize in their initial state.
await syncDocsWait();

// Make a change to each of the two Stores.
store1.setTables({pets: {fido: {species: 'dog'}}});
store2.setValues({open: true});

// Wait for the documents to synchronize in their new state.
await syncDocsWait();

// Ensure the Stores are in sync.
console.log(store1.getContent());
// -> [{pets: {fido: {species: 'dog'}}}, {open: true}]
console.log(store2.getContent());
// -> [{pets: {fido: {species: 'dog'}}}, {open: true}]

persister1.destroy();
persister2.destroy();
Since

v4.0.0

persister-browser

The persister-browser module of the TinyBase project lets you save and load Store data to and from browser storage.

Two entry points are provided, each of which returns a new Persister object that can load and save a Store:

See also

Persistence guides

Since

v1.0.0

Interfaces

These are the interfaces within the persister-browser module.

LocalPersister

The LocalPersister interface represents a Persister that lets you save and load Store data to and from the browser's local storage.

It is a minor extension to the Persister interface and simply provides an extra getStorageName method for accessing the unique key of the storage location the Store is being persisted to.

You should use the createLocalPersister function to create a LocalPersister object.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the LocalPersister interface. There are only two getter methods, getStore and getStorageName.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store | MergeableStore
returnsStore | MergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getStorageName

The getStorageName method returns the unique key of the storage location the Store is being persisted to.

getStorageName(): string
returnsstring

The unique key of the storage location.

Example

This example creates a Persister object against a newly-created Store and then gets the unique key of the storage location back out again.

import {createLocalPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createLocalPersister(store, 'pets');

console.log(persister.getStorageName());
// -> 'pets'

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the LocalPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOrMergeableStore>): string
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the LocalPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<LocalPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<LocalPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the LocalPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<LocalPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<LocalPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<LocalPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<LocalPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the LocalPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<LocalPersister>
returnsPromise<LocalPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<LocalPersister>
returnsPromise<LocalPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the LocalPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

SessionPersister

The SessionPersister interface represents a Persister that lets you save and load Store data to and from the browser's session storage.

You should use the createSessionPersister function to create a SessionPersister object.

It is a minor extension to the Persister interface and simply provides an extra getStorageName method for accessing the unique key of the storage location the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the SessionPersister interface. There are only two getter methods, getStore and getStorageName.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store | MergeableStore
returnsStore | MergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getStorageName

The getStorageName method returns the unique key of the storage location the Store is being persisted to.

getStorageName(): string
returnsstring

The unique key of the storage location.

Example

This example creates a Persister object against a newly-created Store and then gets the unique key of the storage location back out again.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

console.log(persister.getStorageName());
// -> 'pets'

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the SessionPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOrMergeableStore>): string
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the SessionPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<SessionPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<SessionPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the SessionPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<SessionPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<SessionPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<SessionPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<SessionPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the SessionPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<SessionPersister>
returnsPromise<SessionPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<SessionPersister>
returnsPromise<SessionPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the SessionPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

These are the functions within the persister-browser module.

createLocalPersister

The createLocalPersister function creates a LocalPersister object that can persist the Store to the browser's local storage.

createLocalPersister(
  store: Store | MergeableStore,
  storageName: string,
  onIgnoredError?: (error: any) => void,
): LocalPersister
TypeDescription
storeStore | MergeableStore

The Store or MergeableStore to persist.

storageNamestring

The unique key to identify the storage location.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsLocalPersister

A reference to the new LocalPersister object.

A LocalPersister supports both regular Store and MergeableStore objects.

As well as providing a reference to the Store to persist, you must provide a storageName parameter which is unique to your application. This is the key that the browser uses to identify the storage location.

Example

This example creates a LocalPersister object and persists the Store to the browser's local storage.

import {createLocalPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createLocalPersister(store, 'pets');

await persister.save();
console.log(localStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
localStorage.clear();
Since

v1.0.0

createSessionPersister

The createSessionPersister function creates a SessionPersister object that can persist the Store to the browser's session storage.

createSessionPersister(
  store: Store | MergeableStore,
  storageName: string,
  onIgnoredError?: (error: any) => void,
): SessionPersister
TypeDescription
storeStore | MergeableStore

The Store or MergeableStore to persist.

storageNamestring

The unique key to identify the storage location.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsSessionPersister

A reference to the new SessionPersister object.

A SessionPersister supports both regular Store and MergeableStore objects.

As well as providing a reference to the Store to persist, you must provide a storageName parameter which is unique to your application. This is the key that the browser uses to identify the storage location.

Example

This example creates a SessionPersister object and persists the Store to the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

persister-cr-sqlite-wasm

The persister-cr-sqlite-wasm module of the TinyBase project lets you save and load Store data to and from a local CR-SQLite database (in an appropriate environment).

See also

Database Persistence guide

Since

v4.0.0

Interfaces

There is one interface, CrSqliteWasmPersister, within the persister-cr-sqlite-wasm module.

CrSqliteWasmPersister

The CrSqliteWasmPersister interface represents a Persister that lets you save and load Store data to and from a local CR-SQLite database.

You should use the createCrSqliteWasmPersister function to create a CrSqliteWasmPersister object.

It is a minor extension to the Persister interface and simply provides an extra getDb method for accessing a reference to the database instance the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the CrSqliteWasmPersister interface. There are only two getter methods, getStore and getDb.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getDb

The getDb method returns a reference to the database instance the Store is being persisted to.

getDb(): DB
returnsDB

A reference to the database instance.

Example

This example creates a Persister object against a newly-created Store and then gets the database instance back out again.

import {createCrSqliteWasmPersister} from 'tinybase/persisters/persister-cr-sqlite-wasm';
import {createStore} from 'tinybase';
import initWasm from '@vlcn.io/crsqlite-wasm';

const crSqlite3 = await initWasm();
const db = await crSqlite3.open();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createCrSqliteWasmPersister(store, db, 'my_tinybase');

console.log(persister.getDb() == db);
// -> true

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the CrSqliteWasmPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the CrSqliteWasmPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<CrSqliteWasmPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<CrSqliteWasmPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the CrSqliteWasmPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<CrSqliteWasmPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<CrSqliteWasmPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<CrSqliteWasmPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<CrSqliteWasmPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the CrSqliteWasmPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<CrSqliteWasmPersister>
returnsPromise<CrSqliteWasmPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<CrSqliteWasmPersister>
returnsPromise<CrSqliteWasmPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the CrSqliteWasmPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createCrSqliteWasmPersister, within the persister-cr-sqlite-wasm module.

createCrSqliteWasmPersister

The createCrSqliteWasmPersister function creates a CrSqliteWasmPersister object that can persist the Store to a local CR-SQLite database.

createCrSqliteWasmPersister(
  store: Store,
  db: DB,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): CrSqliteWasmPersister
TypeDescription
storeStore

The Store to persist.

dbDB

The database instance that was returned from crSqlite3.open(...).

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment, since v4.0.4.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsCrSqliteWasmPersister

A reference to the new CrSqliteWasmPersister object.

A CrSqliteWasmPersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide a db parameter which identifies the database instance.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The third argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the third argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

Examples

This example creates a CrSqliteWasmPersister object and persists the Store to a local CR-SQLite database as a JSON serialization into the my_tinybase table. It makes a change to the database directly and then reloads it back into the Store.

import {createCrSqliteWasmPersister} from 'tinybase/persisters/persister-cr-sqlite-wasm';
import {createStore} from 'tinybase';
import initWasm from '@vlcn.io/crsqlite-wasm';

const crSqlite3 = await initWasm();
const db = await crSqlite3.open();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createCrSqliteWasmPersister(store, db, 'my_tinybase');

await persister.save();
// Store will be saved to the database.

console.log(await db.execO('SELECT * FROM my_tinybase;'));
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

await db.exec(
  'UPDATE my_tinybase SET store = ' +
    `'[{"pets":{"felix":{"species":"cat"}}},{}]' WHERE _id = '_';`,
);
await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();

This example creates a CrSqliteWasmPersister object and persists the Store to a local SQLite database with tabular mapping.

import {createCrSqliteWasmPersister} from 'tinybase/persisters/persister-cr-sqlite-wasm';
import {createStore} from 'tinybase';
import initWasm from '@vlcn.io/crsqlite-wasm';

const crSqlite3 = await initWasm();
const db = await crSqlite3.open();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createCrSqliteWasmPersister(store, db, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log(await db.execO('SELECT * FROM pets;'));
// -> [{_id: 'fido', species: 'dog'}]

await db.exec(`INSERT INTO pets (_id, species) VALUES ('felix', 'cat')`);
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
Since

v4.0.0

persister-electric-sql

The persister-electric-sql module of the TinyBase project lets you save and load Store data to and from a local ElectricSQL database (in an appropriate environment).

See also

Database Persistence guide

Since

v4.6.0

Interfaces

There is one interface, ElectricSqlPersister, within the persister-electric-sql module.

ElectricSqlPersister

The ElectricSqlPersister interface represents a Persister that lets you save and load Store data to and from a local ElectricSQL database.

You should use the createElectricSqlPersister function to create an ElectricSqlPersister object.

It is a minor extension to the Persister interface and simply provides an extra getElectricClient method for accessing a reference to the Electric client the Store is being persisted to.

Since

v4.6.0

Getter methods

This is the collection of getter methods within the ElectricSqlPersister interface. There are only two getter methods, getStore and getElectricClient.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getElectricClient

The getElectricClient method returns a reference to the Electric client the Store is being persisted to.

getElectricClient(): ElectricClient<any>
returnsElectricClient<any>

A reference to the Electric client.

Example

This example creates a Persister object against a newly-created Store and then gets the Electric client back out again.

import {ElectricDatabase, electrify} from 'electric-sql/wa-sqlite';
import {createElectricSqlPersister} from 'tinybase/persisters/persister-electric-sql';
import {createStore} from 'tinybase';
import {schema} from './generated/client';

const electricClient = await electrify(
  await ElectricDatabase.init('electric.db', ''),
  schema,
  {url: import.meta.env.ELECTRIC_SERVICE},
);
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createElectricSqlPersister(
  store,
  electricClient,
  'my_tinybase',
);

console.log(persister.getElectricClient() == electricClient);
// -> true

persister.destroy();
Since

v4.6.0

Listener methods

This is the collection of listener methods within the ElectricSqlPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the ElectricSqlPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<ElectricSqlPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<ElectricSqlPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the ElectricSqlPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<ElectricSqlPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<ElectricSqlPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<ElectricSqlPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<ElectricSqlPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the ElectricSqlPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<ElectricSqlPersister>
returnsPromise<ElectricSqlPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<ElectricSqlPersister>
returnsPromise<ElectricSqlPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the ElectricSqlPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createElectricSqlPersister, within the persister-electric-sql module.

createElectricSqlPersister

The createElectricSqlPersister function creates an ElectricSqlPersister object that can persist a Store to a local ElectricSQL database.

createElectricSqlPersister(
  store: Store,
  electricClient: ElectricClient<any>,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): ElectricSqlPersister
TypeDescription
storeStore

The Store to persist.

electricClientElectricClient<any>

The Electric client that was returned from await electrify(...).

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment.

returnsElectricSqlPersister

A reference to the new ElectricSqlPersister object.

An ElectricSqlPersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide a electricClient parameter which identifies the Electric client.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The third argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the third argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

Examples

This example creates a ElectricSqlPersister object and persists the Store to a local ElectricSql database as a JSON serialization into the my_tinybase table. It makes a change to the database directly and then reloads it back into the Store.

import {ElectricDatabase, electrify} from 'electric-sql/wa-sqlite';
import {createElectricSqlPersister} from 'tinybase/persisters/persister-electric-sql';
import {createStore} from 'tinybase';
import {schema} from './generated/client';

const electricClient = await electrify(
  await ElectricDatabase.init('electric.db', ''),
  schema,
  {url: import.meta.env.ELECTRIC_SERVICE},
);
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createElectricSqlPersister(
  store,
  electricClient,
  'my_tinybase',
);

await persister.save();
// Store will be saved to the database.

console.log(
  await electricClient.db.raw({sql: 'SELECT * FROM my_tinybase;'}),
);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

await electricClient.db.raw({
  sql:
    'UPDATE my_tinybase SET store = ' +
    `'[{"pets":{"felix":{"species":"cat"}}},{}]' WHERE _id = '_';`,
});
await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();

This example creates a ElectricSqlPersister object and persists the Store to a local ElectricSql database with tabular mapping.

import {ElectricDatabase, electrify} from 'electric-sql/wa-sqlite';
import {createElectricSqlPersister} from 'tinybase/persisters/persister-electric-sql';
import {createStore} from 'tinybase';
import {schema} from './generated/client';

const electricClient = await electrify(
  await ElectricDatabase.init('electric.db', ''),
  schema,
  {url: import.meta.env.ELECTRIC_SERVICE},
);
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createElectricSqlPersister(store, electricClient, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log(await electricClient.db.raw({sql: 'SELECT * FROM pets;'}));
// -> [{_id: 'fido', species: 'dog'}]

await electricClient.db.raw({
  sql: `INSERT INTO pets (_id, species) VALUES ('felix', 'cat')`,
});
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
Since

v4.6.0

persister-expo-sqlite

The persister-expo-sqlite module of the TinyBase project lets you save and load Store data to and from a Expo-SQLite database (in an appropriate React Native environment).

As of TinyBase v5.0, this module provides a Persister for the modern version of Expo's SQLite library, designated 'next' as of November 2023 and default as v14.0 by June 2024.

Note that TinyBase support for the legacy version of Expo-SQLite is no longer available.

See also

Database Persistence guide

Since

v4.5.0

Interfaces

There is one interface, ExpoSqlitePersister, within the persister-expo-sqlite module.

ExpoSqlitePersister

The ExpoSqlitePersister interface represents a Persister that lets you save and load Store data to and from a Expo-SQLite database.

You should use the createExpoSqlitePersister function to create an ExpoSqlitePersister object.

It is a minor extension to the Persister interface and simply provides an extra getDb method for accessing a reference to the database instance the Store is being persisted to.

Since

v4.5.0

Getter methods

This is the collection of getter methods within the ExpoSqlitePersister interface. There are only two getter methods, getStore and getDb.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store | MergeableStore
returnsStore | MergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getDb

The getDb method returns a reference to the database instance the Store is being persisted to.

getDb(): SQLiteDatabase
returnsSQLiteDatabase

A reference to the database instance.

Example

This example creates a Persister object against a newly-created Store and then gets the database instance back out again.

import {createExpoSqlitePersister} from 'tinybase/persisters/persister-expo-sqlite';
import {createStore} from 'tinybase';
import {openDatabaseSync} from 'expo-sqlite';

const db = openDatabaseSync('my.db');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createExpoSqlitePersister(store, db, 'my_tinybase');

console.log(persister.getDb() == db);
// -> true

persister.destroy();
Since

v4.5.0

Listener methods

This is the collection of listener methods within the ExpoSqlitePersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOrMergeableStore>): string
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the ExpoSqlitePersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<ExpoSqlitePersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<ExpoSqlitePersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the ExpoSqlitePersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<ExpoSqlitePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<ExpoSqlitePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<ExpoSqlitePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<ExpoSqlitePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the ExpoSqlitePersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<ExpoSqlitePersister>
returnsPromise<ExpoSqlitePersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<ExpoSqlitePersister>
returnsPromise<ExpoSqlitePersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the ExpoSqlitePersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createExpoSqlitePersister, within the persister-expo-sqlite module.

createExpoSqlitePersister

The createExpoSqlitePersister function creates an ExpoSqlitePersister object that can persist the Store to a local Expo-SQLite database.

createExpoSqlitePersister(
  store: Store | MergeableStore,
  db: SQLiteDatabase,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): ExpoSqlitePersister
TypeDescription
storeStore | MergeableStore

The Store or MergeableStore to persist.

dbSQLiteDatabase

The database instance that was returned from SQLite.openDatabase(...).

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment.

returnsExpoSqlitePersister

A reference to the new ExpoSqlitePersister object.

An ExpoSqlitePersister supports regular Store objects, and can also be used to persist the metadata of a MergeableStore when using the JSON serialization mode, as described below.

Note that this Persister is currently experimental as Expo themselves iterate on the underlying database library API.

As well as providing a reference to the Store to persist, you must provide a db parameter which identifies the database instance.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The third argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the third argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

Examples

This example creates a ExpoSqlitePersister object and persists the Store to a local SQLite database as a JSON serialization into the my_tinybase table. It makes a change to the database directly and then reloads it back into the Store.

import {createExpoSqlitePersister} from 'tinybase/persisters/persister-expo-sqlite';
import {createStore} from 'tinybase';
import {openDatabaseSync} from 'expo-sqlite';

const db = openDatabaseSync('my.db');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createExpoSqlitePersister(store, db, 'my_tinybase');

await persister.save();
// Store will be saved to the database.

console.log(
  await new Promise((resolve) =>
    db.allAsync('SELECT * FROM my_tinybase;').then(resolve),
  ),
);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

await new Promise((resolve) =>
  db
    .allAsync(
      'UPDATE my_tinybase SET store = ' +
        `'[{"pets":{"felix":{"species":"cat"}}},{}]' WHERE _id = '_';`,
    )
    .then(resolve),
);
await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();

This example creates a ExpoSqlitePersister object and persists the Store to a local SQLite database with tabular mapping.

import {createExpoSqlitePersister} from 'tinybase/persisters/persister-expo-sqlite';
import {createStore} from 'tinybase';
import {openDatabaseSync} from 'expo-sqlite';

const db = openDatabaseSync('my.db');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createExpoSqlitePersister(store, db, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log(
  await new Promise((resolve) =>
    db.allAsync('SELECT * FROM pets;').then(resolve),
  ),
);
// -> [{_id: 'fido', species: 'dog'}]

await new Promise((resolve) =>
  db
    .allAsync(`INSERT INTO pets (_id, species) VALUES ('felix', 'cat')`)
    .then(resolve),
);
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
Since

v4.5.0

persister-file

The persister-file module of the TinyBase project lets you save and load Store data to and from a local file system (in an appropriate environment).

See also

Persistence guides

Since

v1.0.0

Interfaces

There is one interface, FilePersister, within the persister-file module.

FilePersister

The FilePersister interface represents a Persister that lets you save and load Store data to and from a local file system.

You should use the createFilePersister function to create a FilePersister object.

It is a minor extension to the Persister interface and simply provides an extra getFilePath method for accessing the location of the local file the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the FilePersister interface. There are only two getter methods, getStore and getFilePath.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store | MergeableStore
returnsStore | MergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getFilePath

The getFilePath method returns the location of the local file the Store is being persisted to.

getFilePath(): string
returnsstring

The location of the local file.

Example

This example creates a Persister object against a newly-created Store and then gets the file location back out again.

import {createFilePersister} from 'tinybase/persisters/persister-file';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createFilePersister(store, '/app/persisted.json');

console.log(persister.getFilePath());
// -> '/app/persisted.json'

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the FilePersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOrMergeableStore>): string
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the FilePersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<FilePersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<FilePersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the FilePersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<FilePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<FilePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<FilePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<FilePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the FilePersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<FilePersister>
returnsPromise<FilePersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<FilePersister>
returnsPromise<FilePersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the FilePersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createFilePersister, within the persister-file module.

createFilePersister

The createFilePersister function creates a FilePersister object that can persist the Store to a local file.

createFilePersister(
  store: Store | MergeableStore,
  filePath: string,
  onIgnoredError?: (error: any) => void,
): FilePersister
TypeDescription
storeStore | MergeableStore

The Store or MergeableStore to persist.

filePathstring

The location of the local file to persist the Store to.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsFilePersister

A reference to the new FilePersister object.

A FilePersister supports both regular Store and MergeableStore objects.

As well as providing a reference to the Store to persist, you must provide a filePath parameter which identifies the file to persist it to.

Example

This example creates a FilePersister object and persists the Store to a local file.

import {createFilePersister} from 'tinybase/persisters/persister-file';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createFilePersister(store, '/app/persisted.json');

await persister.save();
// Store JSON will be saved to the file.

await persister.load();
// Store JSON will be loaded from the file.

persister.destroy();
Since

v1.0.0

persister-indexed-db

The persister-indexed-db module of the TinyBase project lets you save and load Store data to and from browser IndexedDB storage.

See also

Persistence guides

Since

v4.2.0

Interfaces

There is one interface, IndexedDbPersister, within the persister-indexed-db module.

IndexedDbPersister

The IndexedDbPersister interface represents a Persister that lets you save and load Store data to and from browser IndexedDB storage.

You should use the createIndexedDbPersister function to create an IndexedDbPersister object.

It is a minor extension to the Persister interface and simply provides an extra getDbName method for accessing the unique key of the IndexedDB the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the IndexedDbPersister interface. There are only two getter methods, getStore and getDbName.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getDbName

The getDbName method returns the unique key of the IndexedDB the Store is being persisted to.

getDbName(): string
returnsstring

The unique key of the IndexedDB.

Example

This example creates a Persister object against a newly-created Store and then gets the unique key of the IndexedDB back out again.

import {createIndexedDbPersister} from 'tinybase/persisters/persister-indexed-db';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createIndexedDbPersister(store, 'petStore');

console.log(persister.getDbName());
// -> 'petStore'

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the IndexedDbPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the IndexedDbPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<IndexedDbPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<IndexedDbPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the IndexedDbPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<IndexedDbPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<IndexedDbPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<IndexedDbPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<IndexedDbPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the IndexedDbPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<IndexedDbPersister>
returnsPromise<IndexedDbPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<IndexedDbPersister>
returnsPromise<IndexedDbPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the IndexedDbPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createIndexedDbPersister, within the persister-indexed-db module.

createIndexedDbPersister

The createIndexedDbPersister function creates an IndexedDbPersister object that can persist a Store to the browser's IndexedDB storage.

createIndexedDbPersister(
  store: Store,
  dbName: string,
  autoLoadIntervalSeconds?: number,
  onIgnoredError?: (error: any) => void,
): IndexedDbPersister
TypeDescription
storeStore

The Store to persist.

dbNamestring

The unique key to identify the IndexedDB to use.

autoLoadIntervalSeconds?number

How often to poll the database when in 'autoLoad' mode, defaulting to 1.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment.

returnsIndexedDbPersister

A reference to the new IndexedDbPersister object.

An IndexedDbPersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide a dbName parameter which is unique to your application. This is the key used to identify which IndexedDB to use.

Within that database, this Persister will create two object stores: one called 't', and one called 'v'. These will contain the Store's tabular and key-value data respectively, using 'k' and 'v' to store the key and value of each entry, as shown in the example.

Note that it is not possible to reactively detect changes to a browser's IndexedDB. If you do choose to enable automatic loading for the Persister (with the startAutoLoad method), it needs to poll the database for changes. The autoLoadIntervalSeconds method is used to indicate how often to do this.

Example

This example creates a IndexedDbPersister object and persists the Store to the browser's IndexedDB storage.

import {createIndexedDbPersister} from 'tinybase/persisters/persister-indexed-db';
import {createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {fido: {species: 'dog'}})
  .setTable('species', {dog: {price: 5}})
  .setValues({open: true});
const persister = createIndexedDbPersister(store, 'petStore');

await persister.save();
// IndexedDB ->
//   database petStore:
//     objectStore t:
//       object 0:
//         k: "pets"
//         v: {fido: {species: dog}}
//       object 1:
//         k: "species"
//         v: {dog: {price: 5}}
//     objectStore v:
//       object 0:
//         k: "open"
//         v: true

persister.destroy();
Since

v4.2.0

persister-libsql

The persister-libsql module of the TinyBase project lets you save and load Store data to and from a local LibSQL database (in an appropriate environment).

See also

Database Persistence guide

Since

v4.7.0

Interfaces

There is one interface, LibSqlPersister, within the persister-libsql module.

LibSqlPersister

The LibSqlPersister interface represents a Persister that lets you save and load Store data to and from a local LibSQL database.

You should use the createLibSqlPersister function to create a LibSqlPersister object.

It is a minor extension to the Persister interface and simply provides an extra getClient method for accessing a reference to the database client the Store is being persisted to.

Since

v4.7.0

Getter methods

This is the collection of getter methods within the LibSqlPersister interface. There are only two getter methods, getStore and getClient.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getClient

The getClient method returns a reference to the database client the Store is being persisted to.

getClient(): Client
returnsClient

A reference to the database client.

Example

This example creates a Persister object against a newly-created Store and then gets the database client back out again.

import {createClient} from '@libsql/client';
import {createLibSqlPersister} from 'tinybase/persisters/persister-libsql';
import {createStore} from 'tinybase';

const client = createClient({url: 'file:my.db'});
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createLibSqlPersister(store, client, 'my_tinybase');

console.log(persister.getClient() == client);
// -> true

persister.destroy();
Since

v4.7.0

Listener methods

This is the collection of listener methods within the LibSqlPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the LibSqlPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<LibSqlPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<LibSqlPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the LibSqlPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<LibSqlPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<LibSqlPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<LibSqlPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<LibSqlPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the LibSqlPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<LibSqlPersister>
returnsPromise<LibSqlPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<LibSqlPersister>
returnsPromise<LibSqlPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the LibSqlPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createLibSqlPersister, within the persister-libsql module.

createLibSqlPersister

The createLibSqlPersister function creates a LibSqlPersister object that can persist a Store to a local LibSQL database.

createLibSqlPersister(
  store: Store,
  client: Client,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): LibSqlPersister
TypeDescription
storeStore

The Store to persist.

clientClient

The database client that was returned from createClient(...).

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment.

returnsLibSqlPersister

A reference to the new LibSqlPersister object.

A LibSqlPersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide a client parameter which identifies the database client.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The third argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the third argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

Examples

This example creates a LibSqlPersister object and persists the Store to a local SQLite database as a JSON serialization into the my_tinybase table. It makes a change to the database directly and then reloads it back into the Store.

import {createClient} from '@libsql/client';
import {createLibSqlPersister} from 'tinybase/persisters/persister-libsql';
import {createStore} from 'tinybase';

const client = createClient({url: 'file:my.db'});
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createLibSqlPersister(store, client, 'my_tinybase');

await persister.save();
// Store will be saved to the database.

console.log((await client.execute('SELECT * FROM my_tinybase;')).rows);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

await client.execute(
  'UPDATE my_tinybase SET store = ' +
    `'[{"pets":{"felix":{"species":"cat"}}},{}]' WHERE _id = '_';`,
);
await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();

This example creates a LibSqlPersister object and persists the Store to a local SQLite database with tabular mapping.

import {createClient} from '@libsql/client';
import {createLibSqlPersister} from 'tinybase/persisters/persister-libsql';
import {createStore} from 'tinybase';

const client = createClient({url: 'file:my.db'});
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createLibSqlPersister(store, client, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log((await client.execute('SELECT * FROM pets;')).rows);
// -> [{_id: 'fido', species: 'dog'}]

await client.execute(
  `INSERT INTO pets (_id, species) VALUES ('felix', 'cat')`,
);
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
Since

v4.7.0

persister-partykit-client

The persister-partykit-client module of the TinyBase project contains the client portion of the PartyKit integration.

It contains a Persister which, when run in a PartyKit client environment, lets you save and load Store data from the client to the durable PartyKit cloud storage of a server (that is using the complementary persister-partykit-server module).

This enables synchronization of the same Store across multiple clients in a PartyKit party room.

Note that both the client and server parts of this Persister are currently experimental as PartyKit is new and still maturing.

See also

Persistence guides

Since

v4.3.0

Interfaces

There is one interface, PartyKitPersister, within the persister-partykit-client module.

PartyKitPersister

The PartyKitPersister interface represents a Persister that lets you save and load Store data from the client to the durable PartyKit cloud storage of a server.

You should use the createPartyKitPersister function to create a PartyKitPersister object.

It is a minor extension to the Persister interface and simply provides an extra getConnection method for accessing the PartySocket the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the PartyKitPersister interface. There are only two getter methods, getStore and getConnection.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getConnection

The getConnection method returns the PartySocket the Store is being persisted to.

getConnection(): PartySocket
returnsPartySocket

The PartySocket.

Example

This example creates a Persister object against a newly-created Store and then gets the PartySocket back out again.

import {PartySocket} from 'partysocket';
import {createPartyKitPersister} from 'tinybase/persisters/persister-partykit-client';
import {createStore} from 'tinybase';

const partySocket = new PartySocket({
  host: '127.0.0.1:1999',
  room: 'my_room',
});
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createPartyKitPersister(store, partySocket);

console.log(persister.getConnection() == partySocket);
// -> true

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the PartyKitPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the PartyKitPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<PartyKitPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<PartyKitPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the PartyKitPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<PartyKitPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<PartyKitPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<PartyKitPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<PartyKitPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the PartyKitPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<PartyKitPersister>
returnsPromise<PartyKitPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<PartyKitPersister>
returnsPromise<PartyKitPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the PartyKitPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createPartyKitPersister, within the persister-partykit-client module.

createPartyKitPersister

The createPartyKitPersister function creates a PartyKitPersister object that can persist the Store to durable PartyKit storage, enabling synchronization of the same Store across multiple clients.

createPartyKitPersister(
  store: Store,
  connection: PartySocket,
  configOrStoreProtocol?: PartyKitPersisterConfig | "http" | "https",
  onIgnoredError?: (error: any) => void,
): PartyKitPersister
TypeDescription
storeStore

The Store to persist.

connectionPartySocket

The PartySocket to use for participating in the PartyKit room.

configOrStoreProtocol?PartyKitPersisterConfig | "http" | "https"

The PartyKitPersisterConfig configuration for the Persister, (or a string to specify a HTTP protocol to use, defaulting to 'https').

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment.

returnsPartyKitPersister

A reference to the new PartyKitPersister object.

A PartyKitPersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide a connection parameter which is a PartyKit PartySocket that you have already instantiated with details of the host and room.

All suitably-equipped TinyBase clients connecting to that room will get to share synchronized Store state.

The server room's Store is considered the source of truth. If it is a newly-created room, then calling the save method on this Persister will initiate it. If, however, there is already a Store present on the server, the save method will fail gracefully.

In general, you are strongly recommended to use the auto-save and auto-load functionality to stay in sync incrementally with the server, as per the example below. This pattern will handle newly-created servers and newly-created clients - and the synchronization involved in joining rooms, leaving them, or temporarily going offline.

See the PartyKit client socket API documentation for more details.

Example

This example creates a PartyKitPersister object and persists the Store to the browser's IndexedDB storage.

import {PartySocket} from 'partysocket';
import {createPartyKitPersister} from 'tinybase/persisters/persister-partykit-client';
import {createStore} from 'tinybase';

const store = createStore()
  .setTable('pets', {fido: {species: 'dog'}})
  .setTable('species', {dog: {price: 5}})
  .setValues({open: true});
const partySocket = new PartySocket({
  host: '127.0.0.1:1999',
  room: 'my_room',
});
const persister = createPartyKitPersister(store, partySocket);
await persister.startAutoLoad();
await persister.startAutoSave();
// Store will now be synchronized with the room.

persister.destroy();
Since

v4.3.0

Type Aliases

There is one type alias, PartyKitPersisterConfig, within the persister-partykit-client module.

PartyKitPersisterConfig

The PartyKitPersisterConfig type describes the configuration of a PartyKit Persister on the client side.

{
  storeProtocol?: "http" | "https";
  storePath?: string;
  messagePrefix?: string;
}
TypeDescription
storeProtocol?"http" | "https"

The HTTP protocol to use (in addition to the websocket channel). This defaults to 'https' but you may wish to use 'http' for local PartyKit development.

storePath?string

The path used to set and get the whole Store over HTTP(S) on the server. This must match the storePath property of the TinyBasePartyKitServerConfig object used on the server. Both default to '/store'.

messagePrefix?string

The prefix at the beginning of the web socket messages sent between the client and the server when synchronizing the Store. Use this to make sure they do not collide with any other message syntax that your room is using. This must match the messagePrefix property of the TinyBasePartyKitServerConfig object used on the server. Both default to an empty string.

The defaults (if used on both the server and client) will work fine, but if you are building more complex PartyKit apps and you need to configure path names, for example, then this is the thing to use.

Example

When applied to a PartyKit Persister, this PartyKitPersisterConfig will load and save a JSON serialization from and to an end point in your room called /my_tinybase, and use HTTP (rather than the default HTTPS) as the protocol.

Note that this would require you to also add the matching storePath setting to the TinyBasePartyKitServerConfig on the server side.

export const partyKitPersisterConfig = {
  storeProtocol: 'http',
  storePath: '/my_tinybase',
};
Since

v4.3.9

persister-partykit-server

The persister-partykit-server module of the TinyBase project contains the server portion of the PartyKit integration.

It contains a class which, when run in a PartyKit server environment, lets you save and load Store data from a client (that is using the complementary persister-partykit-client module) to durable PartyKit cloud storage.

This enables synchronization of the same Store across multiple clients in a PartyKit party room.

Note that both the client and server parts of this Persister are currently experimental as PartyKit is new and still maturing.

See also

Persistence guides

Since

v4.3.0

Classes

There is one class, TinyBasePartyKitServer, within the persister-partykit-server module.

TinyBasePartyKitServer

This extends the PartyKit Server class, which provides a selection of methods you are expected to implement. The TinyBasePartyKitServer implements only two of them, the onMessage method and the onRequest method, as well as the constructor.

If you wish to use TinyBasePartyKitServer as a general PartyKit server, you can implement other methods. But you must remember to call the super implementations of those methods to ensure the TinyBase synchronization stays supported in addition to your own custom functionality. The same applies to the constructor if you choose to implement that.

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

// This is your PartyKit server entry point.
export class MyServer extends TinyBasePartyKitServer {
  constructor(party) {
    super(party);
    // custom constructor code
  }

  async onStart() {
    // no need to call super.onStart()
    console.log('Server started');
  }

  async onMessage(message, connection) {
    await super.onMessage(message, connection);
    // custom onMessage code
  }

  async onRequest(request) {
    // custom onRequest code, else:
    return await super.onRequest(request);
  }
}

See the PartyKit server API documentation for more details.

Since

v4.3.0

Constructors

There is one constructor, constructor, within the TinyBasePartyKitServer class.

constructor
Methods

These are the methods within the TinyBasePartyKitServer class.

Connection methods

This is the collection of connection methods within the TinyBasePartyKitServer class. There are only two connection methods, onMessage and onRequest.

onMessage

The onMessage method is called when the server receives a message from a client.

onMessage(
  message: string,
  connection: Connection,
): Promise<void>
TypeDescription
messagestring
connectionConnection
returnsPromise<void>

If you choose to implement additional functionality in this method, you must remember to call the super implementation to ensure the TinyBase synchronization stays supported.

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  async onMessage(message, connection) {
    await super.onMessage(message, connection);
    // custom onMessage code
  }
}

See the PartyKit server API documentation for more details.

Since

v4.3.0

onRequest

The onRequest method is called when a HTTP request is made to the party URL.

onRequest(request: Request): Promise<Response>
TypeDescription
requestRequest
returnsPromise<Response>

If you choose to implement additional functionality in this method, you must remember to call the super implementation to ensure the TinyBase synchronization stays supported.

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  async onRequest(request) {
    // custom onRequest code, else:
    return await super.onRequest(request);
  }
}

See the PartyKit server API documentation for more details.

Since

v4.3.0

Sanitization methods

This is the collection of sanitization methods within the TinyBasePartyKitServer class. There are 8 sanitization methods in total.

canDelTable

The canDelTable method lets you allow or disallow deletions of a Table stored on the server, as sent from a client.

canDelTable(
  tableId: string,
  connection: Connection,
): Promise<boolean>
TypeDescription
tableIdstring
connectionConnection
returnsPromise<boolean>

This is one of the functions use to sanitize the data that is being sent from a client. Perhaps you might want to make sure the server-stored data adheres to a particular schema, or you might want to make certain data read-only. Remember that you cannot trust the client to only send data that the server considers valid or safe.

This method is passed the Table Id that the client is trying to delete. The connection parameter will be the web socket connection of that client. You can, for instance, use this to distinguish between different users.

Return false from this method to disallow this Table from being deleted on the server, or true to allow it. The default implementation returns true to allow deletion.

Example

The following implementation will strip out any attempts by the client to delete the 'user' Table:

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  canDelTable(tableId) {
    return tableId != 'user';
  }
}
Since

v4.3.12

canSetTable

The canSetTable method lets you allow or disallow any changes to a Table stored on the server, as sent from a client.

canSetTable(
  tableId: string,
  initialSave: boolean,
  requestOrConnection: Request | Connection,
): Promise<boolean>
TypeDescription
tableIdstring
initialSaveboolean
requestOrConnectionRequest | Connection
returnsPromise<boolean>

This is one of the functions use to sanitize the data that is being sent from a client. Perhaps you might want to make sure the server-stored data adheres to a particular schema, or you might want to make certain data read-only. Remember that you cannot trust the client to only send data that the server considers valid or safe.

This method is passed the Table Id that the client is trying to change. The initialSave parameter distinguishes between the first bulk save of the Store to the PartyKit room over HTTP (true), and subsequent incremental updates over a web sockets (false).

The requestOrConnection parameter will either be the HTTP(S) request or the web socket connection, in those two cases respectively. You can, for instance, use this to distinguish between different users.

Since v4.3.13, the final parameter is the Cell previously stored on the server, if any. Use this to distinguish between the addition of a new Cell (in which case it will be undefined) and the updating of an existing one.

Return false from this method to disallow changes to this Table on the server, or true to allow them (subject to subsequent canSetRow method, canDelRow method, canSetCell method, and canSetCell method checks). The default implementation returns true to allow all changes.

Example

The following implementation will strip out any attempts by the client to update any 'user' tabular data after the initial save:

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  canSetTable(tableId, initialSave) {
    return initialSave || tableId != 'user';
  }
}
Since

v4.3.12

canDelRow

The canDelRow method lets you allow or disallow deletions of a Row stored on the server, as sent from a client.

canDelRow(
  tableId: string,
  rowId: string,
  connection: Connection,
): Promise<boolean>
TypeDescription
tableIdstring
rowIdstring
connectionConnection
returnsPromise<boolean>

This is one of the functions use to sanitize the data that is being sent from a client. Perhaps you might want to make sure the server-stored data adheres to a particular schema, or you might want to make certain data read-only. Remember that you cannot trust the client to only send data that the server considers valid or safe.

This method is passed the Table Id and Row Id that the client is trying to delete. The connection parameter will be the web socket connection of that client. You can, for instance, use this to distinguish between different users.

Return false from this method to disallow this Row from being deleted on the server, or true to allow it. The default implementation returns true to allow deletion.

Example

The following implementation will strip out any attempts by the client to delete the 'me' Row of the 'user' Table:

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  canDelRow(tableId, rowId) {
    return tableId != 'user' || rowId != 'me';
  }
}
Since

v4.3.12

canSetRow

The canSetRow method lets you allow or disallow any changes to a Row stored on the server, as sent from a client.

canSetRow(
  tableId: string,
  rowId: string,
  initialSave: boolean,
  requestOrConnection: Request | Connection,
): Promise<boolean>
TypeDescription
tableIdstring
rowIdstring
initialSaveboolean
requestOrConnectionRequest | Connection
returnsPromise<boolean>

This is one of the functions use to sanitize the data that is being sent from a client. Perhaps you might want to make sure the server-stored data adheres to a particular schema, or you might want to make certain data read-only. Remember that you cannot trust the client to only send data that the server considers valid or safe.

This method is passed the Table Id and Row Id that the client is trying to change. The initialSave parameter distinguishes between the first bulk save of the Store to the PartyKit room over HTTP (true), and subsequent incremental updates over a web sockets (false).

The final requestOrConnection parameter will either be the HTTP(S) request or the web socket connection, in those two cases respectively. You can, for instance, use this to distinguish between different users.

Return false from this method to disallow changes to this Row on the server, or true to allow them (subject to subsequent canSetCell method and canSetCell method checks). The default implementation returns true to allow all changes.

Example

The following implementation will strip out any attempts by the client to update the 'me' Row of the 'user' Table after the initial save:

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  canSetRow(tableId, rowId, initialSave) {
    return initialSave || tableId != 'user' || rowId != 'me';
  }
}
Since

v4.3.12

canDelCell

The canDelCell method lets you allow or disallow deletions of a Cell stored on the server, as sent from a client.

canDelCell(
  tableId: string,
  rowId: string,
  cellId: string,
  connection: Connection,
): Promise<boolean>
TypeDescription
tableIdstring
rowIdstring
cellIdstring
connectionConnection
returnsPromise<boolean>

This is one of the functions use to sanitize the data that is being sent from a client. Perhaps you might want to make sure the server-stored data adheres to a particular schema, or you might want to make certain data read-only. Remember that you cannot trust the client to only send data that the server considers valid or safe.

This method is passed the Table Id, Row Id, and Cell Id that the client is trying to delete. The connection parameter will be the web socket connection of that client. You can, for instance, use this to distinguish between different users.

Return false from this method to disallow this Cell from being deleted on the server, or true to allow it. The default implementation returns true to allow deletion.

Example

The following implementation will strip out any attempts by the client to delete the 'name' Cell of the 'me' Row of the 'user' Table:

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  canDelCell(tableId, rowId, cellId) {
    return tableId != 'user' || rowId != 'me' || cellId != 'name';
  }
}
Since

v4.3.12

canSetCell

The canSetCell method lets you allow or disallow any changes to a Cell stored on the server, as sent from a client.

canSetCell(
  tableId: string,
  rowId: string,
  cellId: string,
  cell: Cell,
  initialSave: boolean,
  requestOrConnection: Request | Connection,
  oldCell: CellOrUndefined,
): Promise<boolean>
TypeDescription
tableIdstring
rowIdstring
cellIdstring
cellCell
initialSaveboolean
requestOrConnectionRequest | Connection
oldCellCellOrUndefined
returnsPromise<boolean>

This is one of the functions use to sanitize the data that is being sent from a client. Perhaps you might want to make sure the server-stored data adheres to a particular schema, or you might want to make certain data read-only. Remember that you cannot trust the client to only send data that the server considers valid or safe.

This method is passed the Table Id, Row Id, and Cell Id that the client is trying to change - as well as the Cell value itself. The initialSave parameter distinguishes between the first bulk save of the Store to the PartyKit room over HTTP (true), and subsequent incremental updates over a web sockets (false).

The final requestOrConnection parameter will either be the HTTP(S) request or the web socket connection, in those two cases respectively. You can, for instance, use this to distinguish between different users.

Return false from this method to disallow changes to this Cell on the server, or true to allow them. The default implementation returns true to allow all changes.

Example

The following implementation will strip out any attempts by the client to update the 'name' Cell of the 'me' Row of the 'user' Table after the initial save:

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  canSetCell(tableId, rowId, cellId, cell, initialSave) {
    return (
      initialSave || tableId != 'user' || rowId != 'me' || cellId != 'name'
    );
  }
}
Since

v4.3.12

canDelValue

The canDelValue method lets you allow or disallow deletions of a Value stored on the server, as sent from a client.

canDelValue(
  valueId: string,
  connection: Connection,
): Promise<boolean>
TypeDescription
valueIdstring
connectionConnection
returnsPromise<boolean>

This is one of the functions use to sanitize the data that is being sent from a client. Perhaps you might want to make sure the server-stored data adheres to a particular schema, or you might want to make certain data read-only. Remember that you cannot trust the client to only send data that the server considers valid or safe.

This method is passed the Value Id that the client is trying to delete. The connection parameter will be the web socket connection of that client. You can, for instance, use this to distinguish between different users.

Return false from this method to disallow this Value from being deleted on the server, or true to allow it. The default implementation returns true to allow deletion.

Example

The following implementation will strip out any attempts by the client to delete the 'userId' Value:

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  canDelValue(valueId) {
    return valueId != 'userId';
  }
}
Since

v4.3.12

canSetValue

The canSetValue method lets you allow or disallow any changes to a Value stored on the server, as sent from a client.

canSetValue(
  valueId: string,
  value: Value,
  initialSave: boolean,
  requestOrConnection: Request | Connection,
  oldValue: ValueOrUndefined,
): Promise<boolean>
TypeDescription
valueIdstring
valueValue
initialSaveboolean
requestOrConnectionRequest | Connection
oldValueValueOrUndefined
returnsPromise<boolean>

This is one of the functions use to sanitize the data that is being sent from a client. Perhaps you might want to make sure the server-stored data adheres to a particular schema, or you might want to make certain data read-only. Remember that you cannot trust the client to only send data that the server considers valid or safe.

This method is passed the Value Id that the client is trying to change - as well as the Value itself. The initialSave parameter distinguishes between the first bulk save of the Store to the PartyKit room over HTTP (true), and subsequent incremental updates over a web sockets (false).

The requestOrConnection parameter will either be the HTTP(S) request or the web socket connection, in those two cases respectively. You can, for instance, use this to distinguish between different users.

Since v4.3.13, the final parameter is the Value previously stored on the server, if any. Use this to distinguish between the addition of a new Value (in which case it will be undefined) and the updating of an existing one.

Return false from this method to disallow changes to this Value on the server, or true to allow them. The default implementation returns true to allow all changes.

Example

The following implementation will strip out any attempts by the client to update the 'userId' Value after the initial save:

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  canSetValue(valueId, value, initialSave) {
    return initialSave || valueId != 'userId';
  }
}
Since

v4.3.12

Properties

There is one property, config, within the TinyBasePartyKitServer class.

config

See the documentation for that type for more details.

Since

v4.3.9

Functions

These are the functions within the persister-partykit-server module.

Connection functions

This is the collection of connection functions within the persister-partykit-server module. There is only one function, broadcastChanges.

broadcastChanges

The broadcastChanges function allows you to broadcast Store changes to all the client connections of a TinyBasePartyKitServer.

broadcastChanges(
  server: TinyBasePartyKitServer,
  changes: Changes,
  without?: string[],
): Promise<void>
TypeDescription
serverTinyBasePartyKitServer

A reference to the TinyBasePartyKitServer object.

changesChanges

The Store changes to broadcast to the server's clients.

without?string[]

An optional array of client connection Ids to exclude from the broadcast.

returnsPromise<void>

This is intended for specialist applications that require the ability to update clients of a TinyBasePartyKitServer in their own custom ways.

The function is asynchronous, so you should use the await keyword or handle its completion as a promise.

Since

v4.5.1

Storage functions

This is the collection of storage functions within the persister-partykit-server module. There are only two storage functions, hasStoreInStorage and loadStoreFromStorage.

hasStoreInStorage

The hasStoreInStorage function returns a boolean indicating whether durable PartyKit storage contains a serialized Store.

hasStoreInStorage(
  storage: Storage,
  storagePrefix?: string,
): Promise<boolean>
TypeDescription
storageStorage

A reference to the storage object, as would normally be accessible from the TinyBasePartyKitServer.party object.

storagePrefix?string

An optional prefix used before all the keys in the server's durable storage, to match the equivalent property in the server's TinyBasePartyKitServerConfig.

returnsPromise<boolean>

A promised boolean indicating whether a Store is present in the storage.

This is intended for specialist applications that require the ability to inspect or load a TinyBase Store from a server's storage outside of the normal context of a TinyBasePartyKitServer.

The function is asynchronous, so you should use the await keyword or handle the result as a promise.

Since

v4.4.1

loadStoreFromStorage

The loadStoreFromStorage function returns the content of a Store from durable PartyKit storage.

loadStoreFromStorage(
  storage: Storage,
  storagePrefix?: string,
): Promise<Content>
TypeDescription
storageStorage

A reference to the storage object, as would normally be accessible from the TinyBasePartyKitServer.party object.

storagePrefix?string

An optional prefix used before all the keys in the server's durable storage, to match the equivalent property in the server's TinyBasePartyKitServerConfig.

returnsPromise<Content>

A promised array of a Tables object and a Values object.

This is intended for specialist applications that require the ability to inspect or load a TinyBase Store from a server's storage outside of the normal context of a TinyBasePartyKitServer.

The function is asynchronous, so you should use the await keyword or handle the result as a promise.

Since

v4.4.1

Type Aliases

There is one type alias, TinyBasePartyKitServerConfig, within the persister-partykit-server module.

TinyBasePartyKitServerConfig

The TinyBasePartyKitServerConfig type describes the configuration of a PartyKit Persister on the server side.

{
  storePath?: string;
  messagePrefix?: string;
  storagePrefix?: string;
  responseHeaders?: HeadersInit;
}
TypeDescription
storePath?string

The path used to set and get the whole Store over HTTP(S) on the server. This must match the storePath property of the PartyKitPersisterConfig used on the client. Both default to '/store'.

messagePrefix?string

The prefix at the beginning of the web socket messages between the client and the server when synchronizing the Store. Use this to make sure they do not collide with any other message syntax that your room is using. This must match the messagePrefix property of the PartyKitPersisterConfig object used on the client. Both default to an empty string.

storagePrefix?string

The prefix used before all the keys in the server's durable storage. Use this in case you are worried about the Store data colliding with other data stored in the room. Defaults to an empty string.

responseHeaders?HeadersInit

An object containing the extra HTTP(S) headers returned to the client from this server. This defaults to the following three headers to allow CORS.

The defaults (if used on both the server and client) will work fine, but if you are building more complex PartyKit apps and you need to configure path names, for example, then this is the type to use.

Example

When set as the config in a TinyBasePartyKitServer, this TinyBasePartyKitServerConfig will expect clients to load and save their JSON serialization from and to an end point in the room called /my_tinybase. Note that this would require you to also add the matching storePath setting to the PartyKitPersisterConfig on the client side.

It will also store the data in the durable storage with a prefix of 'tinybase_' in case you are worried about colliding with other data stored in the room.

import {TinyBasePartyKitServer} from 'tinybase/persisters/persister-partykit-server';

export class MyServer extends TinyBasePartyKitServer {
  readonly config = {
    storePath: '/my_tinybase',
    storagePrefix: 'tinybase_',
  };
}
Since

v4.3.9

persister-pglite

The persister-pglite module of the TinyBase project lets you save and load Store data to and from a PGlite database (in an appropriate environment).

See also

Database Persistence guide

Since

5.2.0

Interfaces

There is one interface, PglitePersister, within the persister-pglite module.

PglitePersister

The PglitePersister interface represents a Persister that lets you save and load Store data to and from a local PGlite database.

You should use the createPglitePersister function to create a PglitePersister object.

It is a minor extension to the Persister interface and simply provides an extra getPglite method for accessing a reference to the database connection the Store is being persisted to.

Since

5.2.0

Getter methods

This is the collection of getter methods within the PglitePersister interface. There are only two getter methods, getStore and getPglite.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store | MergeableStore
returnsStore | MergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getPglite

The getPglite method returns a reference to the database connection the Store is being persisted to.

getPglite(): PGlite
returnsPGlite

A reference to the database connection.

Example

This example creates a Persister object against a newly-created Store and then gets the database connection back out again.

import {PGlite} from '@electric-sql/pglite';
import {createPglitePersister} from 'tinybase/persisters/persister-pglite';
import {createStore} from 'tinybase';

const pglite = await PGlite.create();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = await createPglitePersister(
  store,
  pglite,
  'my_tinybase',
);

console.log(persister.getPglite() == pglite);
// -> true

persister.destroy();
await pglite.close();
Since

5.2.0

Listener methods

This is the collection of listener methods within the PglitePersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOrMergeableStore>): string
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the PglitePersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<PglitePersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<PglitePersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the PglitePersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<PglitePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<PglitePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<PglitePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<PglitePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the PglitePersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<PglitePersister>
returnsPromise<PglitePersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<PglitePersister>
returnsPromise<PglitePersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the PglitePersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createPglitePersister, within the persister-pglite module.

createPglitePersister

The createPglitePersister function creates a PglitePersister object that can persist the Store to a local PGlite database.

createPglitePersister(
  store: Store | MergeableStore,
  pglite: PGlite,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): Promise<PglitePersister>
TypeDescription
storeStore | MergeableStore

The Store or MergeableStore to persist.

pglitePGlite

The database connection that was returned from PGlite.create(...).

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment.

returnsPromise<PglitePersister>

A reference to the new PglitePersister object.

A PglitePersister supports regular Store objects, and can also be used to persist the metadata of a MergeableStore when using the JSON serialization mode, as described below.

As well as providing a reference to the Store to persist, you must provide a pglite parameter which identifies the database connection.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The third argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the third argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

This method is asynchronous because it will await the creation of dedicated new connections to the database. You will need to await a call to this function or handle the return type natively as a Promise.

Examples

This example creates a PglitePersister object and persists the Store to a local PGlite database as a JSON serialization into the my_tinybase table. It makes a change to the database directly and then reloads it back into the Store.

import {PGlite} from '@electric-sql/pglite';
import {createPglitePersister} from 'tinybase/persisters/persister-pglite';
import {createStore} from 'tinybase';

const pglite = await PGlite.create();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = await createPglitePersister(
  store,
  pglite,
  'my_tinybase',
);
await persister.save();
// Store will be saved to the database.

console.log((await pglite.query('SELECT * FROM my_tinybase;')).rows);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

await pglite.query(`UPDATE my_tinybase SET store = $1 WHERE _id = '_';`, [
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
]);

await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();
await pglite.close();

This example creates a PglitePersister object and persists the Store to a local PGlite database with tabular mapping.

import {PGlite} from '@electric-sql/pglite';
import {createPglitePersister} from 'tinybase/persisters/persister-pglite';
import {createStore} from 'tinybase';

const pglite = await PGlite.create();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = await createPglitePersister(store, pglite, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log((await pglite.query('SELECT * FROM pets;')).rows);
// -> [{_id: 'fido', species: '"dog"'}]
// Note that Cells and Values are JSON-encoded in PostgreSQL databases.

await pglite.query(
  `INSERT INTO pets (_id, species) VALUES ('felix', '"cat"')`,
);
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
await pglite.query('DROP TABLE IF EXISTS pets');
await pglite.close();
Since

5.2.0

persister-postgres

The persister-postgres module of the TinyBase project lets you save and load Store data to and from a PostgreSQL database (in an appropriate environment).

See also

Database Persistence guide

Since

5.2.0

Interfaces

There is one interface, PostgresPersister, within the persister-postgres module.

PostgresPersister

The PostgresPersister interface represents a Persister that lets you save and load Store data to and from a local PostgreSQL database.

You should use the createPostgresPersister function to create a PostgresPersister object.

It is a minor extension to the Persister interface and simply provides an extra getSql method for accessing a reference to the database connection the Store is being persisted to.

Since

5.2.0

Getter methods

This is the collection of getter methods within the PostgresPersister interface. There are only two getter methods, getStore and getSql.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store | MergeableStore
returnsStore | MergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getSql

The getSql method returns a reference to the database connection the Store is being persisted to.

getSql(): Sql<{}>
returnsSql<{}>

A reference to the database connection.

Example

This example creates a Persister object against a newly-created Store and then gets the database connection back out again.

import {createPostgresPersister} from 'tinybase/persisters/persister-postgres';
import {createStore} from 'tinybase';
import postgres from 'postgres';

const sql = postgres('postgres://localhost:5432/tinybase');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = await createPostgresPersister(store, sql, 'my_tinybase');

console.log(persister.getSql() == sql);
// -> true

persister.destroy();
await sql.end();
Since

5.2.0

Listener methods

This is the collection of listener methods within the PostgresPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOrMergeableStore>): string
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the PostgresPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<PostgresPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<PostgresPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the PostgresPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<PostgresPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<PostgresPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<PostgresPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<PostgresPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the PostgresPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<PostgresPersister>
returnsPromise<PostgresPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<PostgresPersister>
returnsPromise<PostgresPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the PostgresPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createPostgresPersister, within the persister-postgres module.

createPostgresPersister

The createPostgresPersister function creates a PostgresPersister object that can persist the Store to a local PostgreSQL database.

createPostgresPersister(
  store: Store | MergeableStore,
  sql: Sql<{}>,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): Promise<PostgresPersister>
TypeDescription
storeStore | MergeableStore

The Store or MergeableStore to persist.

sqlSql<{}>

The database connection that was returned from postgres(...).

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment.

returnsPromise<PostgresPersister>

A reference to the new PostgresPersister object.

A PostgresPersister supports regular Store objects, and can also be used to persist the metadata of a MergeableStore when using the JSON serialization mode, as described below.

As well as providing a reference to the Store to persist, you must provide a sql parameter which identifies the database connection.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The third argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the third argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

This method is asynchronous because it will await the creation of dedicated new connections to the database. You will need to await a call to this function or handle the return type natively as a Promise.

Examples

This example creates a PostgresPersister object and persists the Store to a local PostgreSQL database as a JSON serialization into the my_tinybase table. It makes a change to the database directly and then reloads it back into the Store.

import {createPostgresPersister} from 'tinybase/persisters/persister-postgres';
import {createStore} from 'tinybase';
import postgres from 'postgres';

const sql = postgres('postgres://localhost:5432/tinybase');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = await createPostgresPersister(store, sql, 'my_tinybase');

await persister.save();
// Store will be saved to the database.

console.log(await sql`SELECT * FROM my_tinybase;`);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

const json = '[{"pets":{"felix":{"species":"cat"}}},{}]';
await sql`UPDATE my_tinybase SET store = ${json} WHERE _id = '_';`;

await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();
await sql.end();

This example creates a PostgresPersister object and persists the Store to a local PostgreSQL database with tabular mapping.

import {createPostgresPersister} from 'tinybase/persisters/persister-postgres';
import {createStore} from 'tinybase';
import postgres from 'postgres';

const sql = postgres('postgres://localhost:5432/tinybase');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = await createPostgresPersister(store, sql, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log(await sql`SELECT * FROM pets;`);
// -> [{_id: 'fido', species: '"dog"'}]
// Note that Cells and Values are JSON-encoded in PostgreSQL databases.

await sql`INSERT INTO pets (_id, species) VALUES ('felix', '"cat"')`;
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
await sql`DROP TABLE IF EXISTS pets`;
await sql.end();
Since

5.2.0

persister-powersync

The persister-powersync module of the TinyBase project lets you save and load Store data to and from a local SQLite database that is automatically synced using the PowerSync service.

See also

Database Persistence guide

Since

v4.8.0

Interfaces

There is one interface, PowerSyncPersister, within the persister-powersync module.

PowerSyncPersister

The PowerSyncPersister interface represents a Persister that lets you save and load Store data to and from a local SQLite database that is automatically synced using the PowerSync service.

You should use the createPowerSyncPersister function to create a PowerSyncPersister object.

It is a minor extension to the Persister interface and simply provides an extra getPowerSync method for accessing a reference to the PowerSync instance the Store is being persisted to.

Since

v4.8.0

Getter methods

This is the collection of getter methods within the PowerSyncPersister interface. There are only two getter methods, getStore and getPowerSync.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getPowerSync

The getPowerSync method returns a reference to the PowerSync instance the Store is being persisted to.

getPowerSync(): AbstractPowerSyncDatabase
returnsAbstractPowerSyncDatabase

A reference to the PowerSync instance.

Example

This example creates a Persister object against a newly-created Store and then gets the PowerSync instance back out again.

import {createPowerSyncPersister} from 'tinybase/persisters/persister-powersync';
import {createStore} from 'tinybase';
import {usePowerSync} from '@powersync/react';

const ps = usePowerSync();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createPowerSyncPersister(store, ps, 'my_tinybase');

console.log(persister.getPowerSync() == ps);
// -> true

persister.destroy();
Since

v4.8.0

Listener methods

This is the collection of listener methods within the PowerSyncPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the PowerSyncPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<PowerSyncPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<PowerSyncPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the PowerSyncPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<PowerSyncPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<PowerSyncPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<PowerSyncPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<PowerSyncPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the PowerSyncPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<PowerSyncPersister>
returnsPromise<PowerSyncPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<PowerSyncPersister>
returnsPromise<PowerSyncPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the PowerSyncPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createPowerSyncPersister, within the persister-powersync module.

createPowerSyncPersister

The createPowerSyncPersister function creates a PowerSyncPersister object that can persist the Store to a local SQLite database that is automatically synced using the PowerSync service.

createPowerSyncPersister(
  store: Store,
  powerSync: AbstractPowerSyncDatabase,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): PowerSyncPersister
TypeDescription
storeStore

The Store to persist.

powerSyncAbstractPowerSyncDatabase

The PowerSync instance.

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment, since v4.0.4.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsPowerSyncPersister

A reference to the new PowerSyncPersister object.

A PowerSyncPersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide a powerSync parameter which identifies the PowerSync instance.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The third argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the third argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

Examples

This example creates a PowerSyncPersister object and persists the Store to a local PowerSync instance. It makes a change to the database directly and then reloads it back into the Store.

import {createPowerSyncPersister} from 'tinybase/persisters/persister-powersync';
import {createStore} from 'tinybase';
import {usePowerSync} from '@powersync/react';

const ps = usePowerSync();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createPowerSyncPersister(store, ps, 'my_tinybase');

await persister.save();
// Store will be saved to the database.

console.log(
  await new Promise((resolve) =>
    ps.execute('SELECT * FROM my_tinybase;', (_, rows) => resolve(rows)),
  ),
);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

await new Promise((resolve) =>
  ps.execute(
    'UPDATE my_tinybase SET store = ' +
      `'[{"pets":{"felix":{"species":"cat"}}},{}]' WHERE _id = '_';`,
    resolve,
  ),
);
await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();

This example creates a PowerSyncPersister object and persists the Store to a local PowerSync instance with tabular mapping.

import {createPowerSyncPersister} from 'tinybase/persisters/persister-powersync';
import {createStore} from 'tinybase';
import {usePowerSync} from '@powersync/react';

const ps = usePowerSync();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createPowerSyncPersister(store, ps, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log(
  await new Promise((resolve) =>
    ps.execute('SELECT * FROM pets;', (_, rows) => resolve(rows)),
  ),
);
// -> [{_id: 'fido', species: 'dog'}]

await new Promise((resolve) =>
  ps.execute(
    `INSERT INTO pets (_id, species) VALUES ('felix', 'cat')`,
    resolve,
  ),
);
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
Since

v4.8.0

persister-remote

The persister-remote module of the TinyBase project lets you save and load Store data to and from a remote server.

See also

Persistence guides

Since

v1.0.0

Interfaces

There is one interface, RemotePersister, within the persister-remote module.

RemotePersister

The RemotePersister interface represents a Persister that lets you save and load Store data to and from a remote server.

You should use the createRemotePersister function to create a RemotePersister object.

It is a minor extension to the Persister interface and simply provides an extra getUrls method for accessing the URLs the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the RemotePersister interface. There are only two getter methods, getStore and getUrls.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getUrls

The getUrls method returns the URLs the Store is being persisted to.

getUrls(): [string, string]
returns[string, string]

The load and save URLs as a two-item array.

Example

This example creates a RemotePersister object against a newly-created Store and then gets the URLs back out again.

import {createRemotePersister} from 'tinybase/persisters/persister-remote';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createRemotePersister(
  store,
  'https://example.com/load',
  'https://example.com/save',
  5,
);

console.log(persister.getUrls());
// -> ['https://example.com/load', 'https://example.com/save']

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the RemotePersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the RemotePersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<RemotePersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<RemotePersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the RemotePersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<RemotePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<RemotePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<RemotePersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<RemotePersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the RemotePersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<RemotePersister>
returnsPromise<RemotePersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<RemotePersister>
returnsPromise<RemotePersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the RemotePersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createRemotePersister, within the persister-remote module.

createRemotePersister

The createRemotePersister function creates a RemotePersister object that can persist the Store to a remote server.

createRemotePersister(
  store: Store,
  loadUrl: string,
  saveUrl: string,
  autoLoadIntervalSeconds?: number,
  onIgnoredError?: (error: any) => void,
): RemotePersister
TypeDescription
storeStore

The Store to persist.

loadUrlstring

The endpoint that supports a GET method to load JSON.

saveUrlstring

The endpoint that supports a POST method to save JSON.

autoLoadIntervalSeconds?number

How often to poll the loadUrl when automatically loading changes from the server, defaulting to 5.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsRemotePersister

A reference to the new RemotePersister object.

A RemotePersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide loadUrl and saveUrl parameters. These identify the endpoints of the server that support the GET method (to fetch the Store JSON to load) and the POST method (to send the Store JSON to save) respectively.

For when you choose to enable automatic loading for the Persister (with the startAutoLoad method), it will poll the loadUrl for changes, using the ETag HTTP header to identify if things have changed. The autoLoadIntervalSeconds method is used to indicate how often to do this.

If you are implementing the server portion of this functionality yourself, remember to ensure that the ETag header changes every time the server's Store content does - otherwise changes will not be detected by the client.

Example

This example creates a RemotePersister object and persists the Store to a remote server.

import {createRemotePersister} from 'tinybase/persisters/persister-remote';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createRemotePersister(
  store,
  'https://example.com/load',
  'https://example.com/save',
  5,
);

await persister.save();
// Store JSON will be sent to server in a POST request.

await persister.load();
// Store JSON will be fetched from server with a GET request.

persister.destroy();
Since

v1.0.0

persister-sqlite-wasm

The persister-sqlite-wasm module of the TinyBase project lets you save and load Store data to and from a local SQLite database (in an appropriate environment).

See also

Database Persistence guide

Since

v4.0.0

Interfaces

There is one interface, SqliteWasmPersister, within the persister-sqlite-wasm module.

SqliteWasmPersister

The SqliteWasmPersister interface represents a Persister that lets you save and load Store data to and from a local SQLite database.

You should use the createSqliteWasmPersister function to create a SqliteWasmPersister object.

It is a minor extension to the Persister interface and simply provides an extra getDb method for accessing a reference to the database instance the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the SqliteWasmPersister interface. There are only two getter methods, getStore and getDb.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store | MergeableStore
returnsStore | MergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getDb

The getDb method returns a reference to the database instance the Store is being persisted to.

getDb(): any
returnsany

A reference to the database instance.

Example

This example creates a Persister object against a newly-created Store and then gets the database instance back out again.

import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';
import {createStore} from 'tinybase';
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';

const sqlite3 = await sqlite3InitModule();
const db = new sqlite3.oo1.DB(':memory:', 'c');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSqliteWasmPersister(
  store,
  sqlite3,
  db,
  'my_tinybase',
);

console.log(persister.getDb() == db);
// -> true

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the SqliteWasmPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOrMergeableStore>): string
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the SqliteWasmPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<SqliteWasmPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<SqliteWasmPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the SqliteWasmPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<SqliteWasmPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<SqliteWasmPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<SqliteWasmPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<SqliteWasmPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the SqliteWasmPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<SqliteWasmPersister>
returnsPromise<SqliteWasmPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<SqliteWasmPersister>
returnsPromise<SqliteWasmPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the SqliteWasmPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createSqliteWasmPersister, within the persister-sqlite-wasm module.

createSqliteWasmPersister

The createSqliteWasmPersister function creates a SqliteWasmPersister object that can persist the Store to a local SQLite database.

createSqliteWasmPersister(
  store: Store | MergeableStore,
  sqlite3: any,
  db: any,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): SqliteWasmPersister
TypeDescription
storeStore | MergeableStore

The Store or MergeableStore to persist.

sqlite3any

The WASM module that was returned from sqlite3InitModule.

dbany

The database instance that was returned from new sqlite3.oo1.DB(...).

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment, since v4.0.4.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsSqliteWasmPersister

A reference to the new SqliteWasmPersister object.

A SqliteWasmPersister supports regular Store objects, and can also be used to persist the metadata of a MergeableStore when using the JSON serialization mode, as described below.

As well as providing a reference to the Store to persist, you must provide sqlite3 and db parameters which identify the WASM module and database instance respectively.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The fourth argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the fourth argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

Examples

This example creates a SqliteWasmPersister object and persists the Store to a local SQLite database as a JSON serialization into the my_tinybase table. It makes a change to the database directly and then reloads it back into the Store.

import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';
import {createStore} from 'tinybase';
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';

const sqlite3 = await sqlite3InitModule();
const db = new sqlite3.oo1.DB(':memory:', 'c');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSqliteWasmPersister(
  store,
  sqlite3,
  db,
  'my_tinybase',
);

await persister.save();
// Store will be saved to the database.

console.log(db.exec('SELECT * FROM my_tinybase;', {rowMode: 'object'}));
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

db.exec(
  'UPDATE my_tinybase SET store = ' +
    `'[{"pets":{"felix":{"species":"cat"}}},{}]' WHERE _id = '_';`,
);
await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();

This example creates a SqliteWasmPersister object and persists the Store to a local SQLite database with tabular mapping.

import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';
import {createStore} from 'tinybase';
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';

const sqlite3 = await sqlite3InitModule();
const db = new sqlite3.oo1.DB(':memory:', 'c');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSqliteWasmPersister(store, sqlite3, db, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log(db.exec('SELECT * FROM pets;', {rowMode: 'object'}));
// -> [{_id: 'fido', species: 'dog'}]

db.exec(`INSERT INTO pets (_id, species) VALUES ('felix', 'cat')`);
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
Since

v4.0.0

persister-sqlite3

The persister-sqlite3 module of the TinyBase project lets you save and load Store data to and from a local SQLite database (in an appropriate environment).

See also

Database Persistence guide

Since

v4.0.0

Interfaces

There is one interface, Sqlite3Persister, within the persister-sqlite3 module.

Sqlite3Persister

The Sqlite3Persister interface represents a Persister that lets you save and load Store data to and from a local SQLite database.

You should use the createSqlite3Persister function to create a Sqlite3Persister object.

It is a minor extension to the Persister interface and simply provides an extra getDb method for accessing a reference to the database instance the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the Sqlite3Persister interface. There are only two getter methods, getStore and getDb.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store | MergeableStore
returnsStore | MergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getDb

The getDb method returns a reference to the database instance the Store is being persisted to.

getDb(): Database
returnsDatabase

A reference to the database instance.

Example

This example creates a Persister object against a newly-created Store and then gets the database instance back out again.

import {Database} from 'sqlite3';
import {createSqlite3Persister} from 'tinybase/persisters/persister-sqlite3';
import {createStore} from 'tinybase';

const db = new Database(':memory:');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSqlite3Persister(store, db, 'my_tinybase');

console.log(persister.getDb() == db);
// -> true

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the Sqlite3Persister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOrMergeableStore>): string
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the Sqlite3Persister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<Sqlite3Persister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<Sqlite3Persister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the Sqlite3Persister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<Sqlite3Persister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<Sqlite3Persister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<Sqlite3Persister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<Sqlite3Persister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the Sqlite3Persister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<Sqlite3Persister>
returnsPromise<Sqlite3Persister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<Sqlite3Persister>
returnsPromise<Sqlite3Persister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the Sqlite3Persister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createSqlite3Persister, within the persister-sqlite3 module.

createSqlite3Persister

The createSqlite3Persister function creates a Sqlite3Persister object that can persist the Store to a local SQLite database.

createSqlite3Persister(
  store: Store | MergeableStore,
  db: Database,
  configOrStoreTableName?: string | DatabasePersisterConfig,
  onSqlCommand?: (sql: string, params?: any[]) => void,
  onIgnoredError?: (error: any) => void,
): Sqlite3Persister
TypeDescription
storeStore | MergeableStore

The Store or MergeableStore to persist.

dbDatabase

The database instance that was returned from new sqlite3.Database(...).

configOrStoreTableName?string | DatabasePersisterConfig

A DatabasePersisterConfig to configure the persistence mode (or a string to set the storeTableName property of the JSON serialization).

onSqlCommand?(sql: string, params?: any[]) => void

An optional handler called every time the Persister executes a SQL command or query. This is suitable for logging persistence behavior in a development environment, since v4.0.4.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsSqlite3Persister

A reference to the new Sqlite3Persister object.

A Sqlite3Persister supports regular Store objects, and can also be used to persist the metadata of a MergeableStore when using the JSON serialization mode, as described below.

As well as providing a reference to the Store to persist, you must provide a db parameter which identifies the database instance.

A database Persister uses one of two modes: either a JSON serialization of the whole Store stored in a single row of a table (the default), or a tabular mapping of Table Ids to database table names and vice-versa).

The third argument is a DatabasePersisterConfig object that configures which of those modes to use, and settings for each. If the third argument is simply a string, it is used as the storeTableName property of the JSON serialization.

See the documentation for the DpcJson and DpcTabular types for more information on how both of those modes can be configured.

Examples

This example creates a Sqlite3Persister object and persists the Store to a local SQLite database as a JSON serialization into the my_tinybase table. It makes a change to the database directly and then reloads it back into the Store.

import {Database} from 'sqlite3';
import {createSqlite3Persister} from 'tinybase/persisters/persister-sqlite3';
import {createStore} from 'tinybase';

const db = new Database(':memory:');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSqlite3Persister(store, db, 'my_tinybase');

await persister.save();
// Store will be saved to the database.

console.log(
  await new Promise((resolve) =>
    db.all('SELECT * FROM my_tinybase;', (_, rows) => resolve(rows)),
  ),
);
// -> [{_id: '_', store: '[{"pets":{"fido":{"species":"dog"}}},{}]'}]

await new Promise((resolve) =>
  db.all(
    'UPDATE my_tinybase SET store = ' +
      `'[{"pets":{"felix":{"species":"cat"}}},{}]' WHERE _id = '_';`,
    resolve,
  ),
);
await persister.load();
console.log(store.getTables());
// -> {pets: {felix: {species: 'cat'}}}

persister.destroy();

This example creates a Sqlite3Persister object and persists the Store to a local SQLite database with tabular mapping.

import {Database} from 'sqlite3';
import {createSqlite3Persister} from 'tinybase/persisters/persister-sqlite3';
import {createStore} from 'tinybase';

const db = new Database(':memory:');
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSqlite3Persister(store, db, {
  mode: 'tabular',
  tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
});

await persister.save();
console.log(
  await new Promise((resolve) =>
    db.all('SELECT * FROM pets;', (_, rows) => resolve(rows)),
  ),
);
// -> [{_id: 'fido', species: 'dog'}]

await new Promise((resolve) =>
  db.all(
    `INSERT INTO pets (_id, species) VALUES ('felix', 'cat')`,
    resolve,
  ),
);
await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

persister.destroy();
Since

v4.0.0

persister-yjs

The persister-yjs module of the TinyBase project provides a way to save and load Store data to and from a Yjs document.

A single entry point, the createYjsPersister function, is provided, which returns a new Persister object that can bind a Store to a provided Yjs document.

See also

Third-Party CRDT Persistence guide

Since

v4.0.0

Interfaces

There is one interface, YjsPersister, within the persister-yjs module.

YjsPersister

The YjsPersister interface represents a Persister that lets you save and load Store data to and from a Yjs document.

You should use the createYjsPersister function to create a YjsPersister object.

It is a minor extension to the Persister interface and simply provides an extra getYDoc method for accessing the Yjs document the Store is being persisted to.

Since

v4.3.14

Getter methods

This is the collection of getter methods within the YjsPersister interface. There are only two getter methods, getStore and getYDoc.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): Store
returnsStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getYDoc

The getYDoc method returns the Yjs document the Store is being persisted to.

getYDoc(): Doc
returnsDoc

The Yjs document.

Example

This example creates a Persister object against a newly-created Store and then gets the Yjs document back out again.

import {Doc} from 'yjs';
import {createStore} from 'tinybase';
import {createYjsPersister} from 'tinybase/persisters/persister-yjs';

const doc = new Doc();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createYjsPersister(store, doc);

console.log(persister.getYDoc() == doc);
// -> true

persister.destroy();
Since

v4.3.14

Listener methods

This is the collection of listener methods within the YjsPersister interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<StoreOnly>): string
TypeDescription
listenerStatusListener<StoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the YjsPersister interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<YjsPersister>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<YjsPersister>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the YjsPersister interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<YjsPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<YjsPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<YjsPersister>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<YjsPersister>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the YjsPersister interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<YjsPersister>
returnsPromise<YjsPersister>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<YjsPersister>
returnsPromise<YjsPersister>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Development methods

This is the collection of development methods within the YjsPersister interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createYjsPersister, within the persister-yjs module.

createYjsPersister

The createYjsPersister function creates a YjsPersister object that can persist the Store to a Yjs document.

createYjsPersister(
  store: Store,
  yDoc: Doc,
  yMapName?: string,
  onIgnoredError?: (error: any) => void,
): YjsPersister
TypeDescription
storeStore

The Store to persist.

yDocDoc

The Yjs document to persist the Store to.

yMapName?string

The name of the Y.Map used inside the Yjs document to sync the Store to (which otherwise will default to 'tinybase').

onIgnoredError?(error: any) => void

An optional handler for the errors that the Persister would otherwise ignore when trying to save or load data. This is suitable for debugging persistence issues in a development environment, since v4.0.4.

returnsYjsPersister

A reference to the new YjsPersister object.

A YjsPersister only supports regular Store objects, and cannot be used to persist the metadata of a MergeableStore.

As well as providing a reference to the Store to persist, you must provide the Yjs document to persist it to.

Examples

This example creates a YjsPersister object and persists the Store to a Yjs document.

import {Doc} from 'yjs';
import {createStore} from 'tinybase';
import {createYjsPersister} from 'tinybase/persisters/persister-yjs';

const doc = new Doc();
const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createYjsPersister(store, doc);

await persister.save();
// Store will be saved to the document.

console.log(doc.toJSON());
// -> {tinybase: {t: {pets: {fido: {species: 'dog'}}}, v: {}}}

persister.destroy();

This more complex example uses Yjs updates to keep two Store objects (each with their own YjsPersister objects and Yjs documents) in sync with each other. We use the await keyword extensively for the purpose of ensuring sequentiality in this example.

Typically, real-world synchronization would happen between two systems via a Yjs connection provider. Here, we synthesize that with the syncDocs function.

import {Doc, applyUpdate, encodeStateAsUpdate} from 'yjs';
import {createStore} from 'tinybase';
import {createYjsPersister} from 'tinybase/persisters/persister-yjs';

const doc1 = new Doc();
const doc2 = new Doc();

// A function to manually synchronize documents with each other. Typically
// this would happen over the wire, via a Yjs connection provider.
const syncDocs = async () => {
  applyUpdate(doc1, encodeStateAsUpdate(doc2));
  applyUpdate(doc2, encodeStateAsUpdate(doc1));
};

// Bind a persisted Store to each document.
const store1 = createStore();
const persister1 = createYjsPersister(store1, doc1);
await persister1.startAutoLoad();
await persister1.startAutoSave();

const store2 = createStore();
const persister2 = createYjsPersister(store2, doc2);
await persister2.startAutoLoad();
await persister2.startAutoSave();

// Synchronize the documents in their initial state.
await syncDocs();

// Make a change to each of the two Stores.
store1.setTables({pets: {fido: {species: 'dog'}}});
store2.setValues({open: true});
// ...

// Synchronize the documents with each other again.
await syncDocs();

// Ensure the Stores are in sync.
console.log(store1.getContent());
// -> [{pets: {fido: {species: 'dog'}}}, {open: true}]
console.log(store2.getContent());
// -> [{pets: {fido: {species: 'dog'}}}, {open: true}]

persister1.destroy();
persister2.destroy();
Since

v4.0.0

synchronizers

The synchronizers module of the TinyBase project lets you synchronize MergeableStore data to and from other MergeableStore instances.

See also

Synchronization guide

Since

v5.0.0

Interfaces

There is one interface, Synchronizer, within the synchronizers module.

Synchronizer

A Synchronizer object lets you synchronize MergeableStore data with another TinyBase client or system.

This is useful for sharing data between users, or between devices of a single user. This is especially valuable when there is the possibility that there has been a lack of immediate connectivity between clients and the synchronization requires some negotiation to orchestrate merging the MergeableStore objects together.

Creating a Synchronizer depends on the choice of underlying medium over which the synchronization will take place. Options include the createWsSynchronizer function (for a Synchronizer that will sync over web-sockets), and the createLocalSynchronizer function (for a Synchronizer that will sync two MergeableStore objects in memory on one system). The createCustomSynchronizer function can also be used to easily create a fully customized way to send and receive the messages of the synchronization protocol.

Note that, as an interface, it is an extension to the Persister interface, since they share underlying implementations. Think of a Synchronizer as 'persisting' your MergeableStore to another client (and vice-versa).

Example

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them. A change in one becomes evident in the other.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

store2.setRow('pets', 'felix', {species: 'cat'});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

Getter methods

This is the collection of getter methods within the Synchronizer interface. There is only one method, getStore.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): MergeableStore
returnsMergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

Listener methods

This is the collection of listener methods within the Synchronizer interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<MergeableStoreOnly>): string
TypeDescription
listenerStatusListener<MergeableStoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the Synchronizer interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<Synchronizer>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<Synchronizer>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the Synchronizer interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<Synchronizer>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<Synchronizer>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<Synchronizer>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<Synchronizer>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the Synchronizer interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<Synchronizer>
returnsPromise<Synchronizer>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<Synchronizer>
returnsPromise<Synchronizer>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Synchronization methods

This is the collection of synchronization methods within the Synchronizer interface. There are only three synchronization methods, getSynchronizerStats, startSync, and stopSync.

getSynchronizerStats

The getSynchronizerStats method provides a set of statistics about the Synchronizer, and is used for debugging purposes.

getSynchronizerStats(): SynchronizerStats
returnsSynchronizerStats

A SynchronizerStats object containing Synchronizer send and receive statistics.

The SynchronizerStats object contains a count of the number of times the Synchronizer has sent and received messages.

The method is intended to be used during development to ensure your synchronization layer is acting as expected, for example.

Example

This example gets the send and receive statistics of two active Synchronizer instances.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
store2.setRow('pets', 'felix', {species: 'cat'});
// ...

console.log(synchronizer1.getSynchronizerStats());
// -> {receives: 4, sends: 5}
console.log(synchronizer2.getSynchronizerStats());
// -> {receives: 5, sends: 4}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

startSync

The startSync method is used to start the process of synchronization between this instance and another matching Synchronizer.

startSync(initialContent?: Content): Promise<Synchronizer>
TypeDescription
initialContent?Content

An optional Content object used when no content is available from another other peer Synchronizer instances.

returnsPromise<Synchronizer>

A Promise containing a reference to the Synchronizer object.

The underlying implementation of a Synchronizer is shared with the Persister framework, and so this startSync method is equivalent to starting both auto-loading (listening to sync messages from other active Synchronizer instances) and auto-saving (sending sync messages to it).

This method is asynchronous so you should you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them. A change in one becomes evident in the other.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

store2.setRow('pets', 'felix', {species: 'cat'});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them with default content. The default content from the first Synchronizer's startSync method ends up populated in both MergeableStore instances: by the time the second started, the first was available to synchronize with and its default was not needed.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync([{pets: {fido: {species: 'dog'}}}, {}]);
await synchronizer2.startSync([{pets: {felix: {species: 'cat'}}}, {}]);

// ...

console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

stopSync

The stopSync method is used to stop the process of synchronization between this instance and another matching Synchronizer.

stopSync(): this
returnsthis

A reference to the Synchronizer object.

The underlying implementation of a Synchronizer is shared with the Persister framework, and so this startSync method is equivalent to stopping both auto-loading (listening to sync messages from other active Synchronizer instances) and auto-saving (sending sync messages to them).

Example

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts - then stops - synchronizing them. Subsequent changes are not merged.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const synchronizer1 = createLocalSynchronizer(store1);
await synchronizer1.startSync();

const store2 = createMergeableStore();
const synchronizer2 = createLocalSynchronizer(store2);
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.stopSync();
synchronizer2.stopSync();

store1.setCell('pets', 'fido', 'color', 'brown');
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

Development methods

This is the collection of development methods within the Synchronizer interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Enumerations

There is one enumeration, Message, within the synchronizers module.

Message

The Message enum is used to indicate the type of the message being passed between Synchronizer instances.

{
  Response: 0;
  GetContentHashes: 1;
  ContentHashes: 2;
  ContentDiff: 3;
  GetTableDiff: 4;
  GetRowDiff: 5;
  GetCellDiff: 6;
  GetValueDiff: 7;
}
ValueDescription
Response0

A message that is a response to a previous request.

GetContentHashes1

A message that is a request to get ContentHashes from another MergeableStore.

ContentHashes2

A message that contains ContentHashes.

ContentDiff3

A message that contains a ContentDiff.

GetTableDiff4

A message that is a request to get a TableDiff from another MergeableStore.

GetRowDiff5

A message that is a request to get a RowDiff from another MergeableStore.

GetCellDiff6

A message that is a request to get a CellDiff from another MergeableStore.

GetValueDiff7

A message that is a request to get a ValueDiff from another MergeableStore.

These message comprise the basic synchronization protocol for merging MergeableStore instances across multiple systems.

The enum is generally intended to be used internally within TinyBase itself and opaque to applications that use synchronization.

Since

v5.0.0

Functions

There is one function, createCustomSynchronizer, within the synchronizers module.

createCustomSynchronizer

The createCustomSynchronizer function creates a Synchronizer object that can persist one MergeableStore to another.

createCustomSynchronizer(
  store: MergeableStore,
  send: Send,
  registerReceive: (receive: Receive) => void,
  destroy: () => void,
  requestTimeoutSeconds: number,
  onSend?: Send,
  onReceive?: Receive,
  onIgnoredError?: (error: any) => void,
): Synchronizer
TypeDescription
storeMergeableStore

The MergeableStore to synchronize.

sendSend

A Send function for sending a message.

registerReceive(receive: Receive) => void

A callback (called once when the Synchronizer is created) that is passed a Receive function that you need to ensure will receive messages addressed or broadcast to this client.

destroy() => void

A function called when destroying the Synchronizer which can be used to clean up underlying resources.

requestTimeoutSecondsnumber

An number of seconds before a request sent from this Synchronizer to another peer times out.

onSend?Send

An optional handler for the messages that this Synchronizer sends. This is suitable for debugging synchronization issues in a development environment, since v5.1.

onReceive?Receive

An optional handler for the messages that this Synchronizer receives. This is suitable for debugging synchronization issues in a development environment, since v5.1.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Synchronizer would otherwise ignore when trying to synchronize data. This is suitable for debugging synchronization issues in a development environment.

returnsSynchronizer

A reference to the new Synchronizer object.

As well as providing a reference to the MergeableStore to synchronize, you must provide parameters which identify how to send and receive changes to and from this MergeableStore and its peers. This is entirely dependent upon the medium of communication used.

You must also provide a callback for when the Synchronizer is destroyed (which is a good place to clean up resources and stop communication listeners), and indicate how long the Synchronizer will wait for responses to message requests before timing out.

A final set of optional handlers can be provided to help debug sends, receives, and errors respectively.

Example

This example creates a function for creating custom Synchronizer objects via a very naive pair of message buses (which are first-in, first-out). Each Synchronizer can write to the other's bus, and they each poll to read from their own. The example then uses these Synchronizer instances to sync two MergeableStore objects together

import {createMergeableStore, getUniqueId} from 'tinybase';
import {createCustomSynchronizer} from 'tinybase/synchronizers';

const bus1 = [];
const bus2 = [];

const createBusSynchronizer = (store, localBus, remoteBus) => {
  let timer;
  const clientId = getUniqueId();
  return createCustomSynchronizer(
    store,
    (toClientId, requestId, message, body) => {
      // send
      remoteBus.push([clientId, requestId, message, body]);
    },
    (receive) => {
      // registerReceive
      timer = setInterval(() => {
        if (localBus.length > 0) {
          receive(...localBus.shift());
        }
      }, 1);
    },
    () => clearInterval(timer), // destroy
    1,
  );
};

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createBusSynchronizer(store1, bus1, bus2);
const synchronizer2 = createBusSynchronizer(store2, bus2, bus1);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
store2.setTables({pets: {felix: {species: 'cat'}}});

// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

Type Aliases

These are the type aliases within the synchronizers module.

Synchronization type aliases

This is the collection of synchronization type aliases within the synchronizers module. There are only two synchronization type aliases, Receive and Send.

Receive

The Receive type describes a function that knows how to handle the arrival of a message as part of the synchronization protocol.

(
  fromClientId: Id,
  requestId: IdOrNull,
  message: Message,
  body: any,
): void
TypeDescription
fromClientIdId

The Id of the other client (in other words, the other system) that sent the message.

requestIdIdOrNull

The optional Id of the message, which should be returned in the response (if requested) to constitute a matched request/response transaction.

messageMessage

A number that indicates the type of the message, according to the Message enum.

bodyany

A message-specific payload.

returnsvoid

This has no return value.

When a message arrives (most likely from another system), the function will be called with parameters that indicate where the message came from, and its meaning and content.

Since

v5.0.0

Send

The Send type describes a function that knows how to dispatch a message as part of the synchronization protocol.

(
  toClientId: IdOrNull,
  requestId: IdOrNull,
  message: Message,
  body: any,
): void
TypeDescription
toClientIdIdOrNull

The optional Id of the other client (in other words, the other system) to which the message should be sent. If omitted, this is to be a broadcast.

requestIdIdOrNull

The optional Id of the message, which should be awaited in the response (if requested) to constitute a matched request/response transaction.

messageMessage

A number that indicates the type of the message, according to the Message enum.

bodyany

A message-specific payload.

returnsvoid

This has no return value.

Since

v5.0.0

Development type aliases

This is the collection of development type aliases within the synchronizers module. There is only one type alias, SynchronizerStats.

SynchronizerStats

The SynchronizerStats type describes the number of times a Synchronizer object has sent or received data.

{
  sends: number;
  receives: number;
}
TypeDescription
sendsnumber

The number of times messages have been sent.

receivesnumber

The number of times messages has been received.

A SynchronizerStats object is returned from the getSynchronizerStats method.

Since

v5.0.0

synchronizer-broadcast-channel

The synchronizer-broadcast-channel module of the TinyBase project lets you synchronize MergeableStore data to and from other MergeableStore instances via a browser's BroadcastChannel API.

See also

Synchronization guide

Since

v5.0.0

Interfaces

There is one interface, BroadcastChannelSynchronizer, within the synchronizer-broadcast-channel module.

BroadcastChannelSynchronizer

The BroadcastChannelSynchronizer interface represents a Synchronizer that lets you synchronize MergeableStore data to and from other MergeableStore instances via a browser's BroadcastChannel API.

You should use the createBroadcastChannelSynchronizer function to create a BroadcastChannelSynchronizer object.

It is a minor extension to the Synchronizer interface and simply provides an extra getChannelName method for accessing the name of the channel being used.

Since

v5.0.0

Getter methods

This is the collection of getter methods within the BroadcastChannelSynchronizer interface. There are only two getter methods, getStore and getChannelName.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): MergeableStore
returnsMergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getChannelName

The getChannelName method returns the name of the channel being used for synchronization.

getChannelName(): string
returnsstring

The channel name.

Example

This example creates a BroadcastChannelSynchronizer object for a newly-created MergeableStore and then gets the channel name back out again.

import {createBroadcastChannelSynchronizer} from 'tinybase/synchronizers/synchronizer-broadcast-channel';
import {createMergeableStore} from 'tinybase';

const store = createMergeableStore();
const synchronizer = createBroadcastChannelSynchronizer(store, 'channelA');

console.log(synchronizer.getChannelName());
// -> 'channelA'

synchronizer.destroy();
Since

v5.0.0

Listener methods

This is the collection of listener methods within the BroadcastChannelSynchronizer interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<MergeableStoreOnly>): string
TypeDescription
listenerStatusListener<MergeableStoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the BroadcastChannelSynchronizer interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<BroadcastChannelSynchronizer>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<BroadcastChannelSynchronizer>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the BroadcastChannelSynchronizer interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<BroadcastChannelSynchronizer>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<BroadcastChannelSynchronizer>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<BroadcastChannelSynchronizer>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<BroadcastChannelSynchronizer>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the BroadcastChannelSynchronizer interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<BroadcastChannelSynchronizer>
returnsPromise<BroadcastChannelSynchronizer>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<BroadcastChannelSynchronizer>
returnsPromise<BroadcastChannelSynchronizer>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Synchronization methods

This is the collection of synchronization methods within the BroadcastChannelSynchronizer interface. There are only three synchronization methods, getSynchronizerStats, startSync, and stopSync.

getSynchronizerStats

The getSynchronizerStats method provides a set of statistics about the Synchronizer, and is used for debugging purposes.

getSynchronizerStats(): SynchronizerStats
returnsSynchronizerStats

A SynchronizerStats object containing Synchronizer send and receive statistics.

The SynchronizerStats object contains a count of the number of times the Synchronizer has sent and received messages.

The method is intended to be used during development to ensure your synchronization layer is acting as expected, for example.

Example

This example gets the send and receive statistics of two active Synchronizer instances.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
store2.setRow('pets', 'felix', {species: 'cat'});
// ...

console.log(synchronizer1.getSynchronizerStats());
// -> {receives: 4, sends: 5}
console.log(synchronizer2.getSynchronizerStats());
// -> {receives: 5, sends: 4}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

startSync

The startSync method is used to start the process of synchronization between this instance and another matching Synchronizer.

startSync(initialContent?: Content): Promise<BroadcastChannelSynchronizer>
TypeDescription
initialContent?Content

An optional Content object used when no content is available from another other peer Synchronizer instances.

returnsPromise<BroadcastChannelSynchronizer>

A Promise containing a reference to the Synchronizer object.

The underlying implementation of a Synchronizer is shared with the Persister framework, and so this startSync method is equivalent to starting both auto-loading (listening to sync messages from other active Synchronizer instances) and auto-saving (sending sync messages to it).

This method is asynchronous so you should you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them. A change in one becomes evident in the other.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

store2.setRow('pets', 'felix', {species: 'cat'});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them with default content. The default content from the first Synchronizer's startSync method ends up populated in both MergeableStore instances: by the time the second started, the first was available to synchronize with and its default was not needed.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync([{pets: {fido: {species: 'dog'}}}, {}]);
await synchronizer2.startSync([{pets: {felix: {species: 'cat'}}}, {}]);

// ...

console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

stopSync

The stopSync method is used to stop the process of synchronization between this instance and another matching Synchronizer.

stopSync(): this
returnsthis

A reference to the Synchronizer object.

The underlying implementation of a Synchronizer is shared with the Persister framework, and so this startSync method is equivalent to stopping both auto-loading (listening to sync messages from other active Synchronizer instances) and auto-saving (sending sync messages to them).

Example

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts - then stops - synchronizing them. Subsequent changes are not merged.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const synchronizer1 = createLocalSynchronizer(store1);
await synchronizer1.startSync();

const store2 = createMergeableStore();
const synchronizer2 = createLocalSynchronizer(store2);
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.stopSync();
synchronizer2.stopSync();

store1.setCell('pets', 'fido', 'color', 'brown');
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

Development methods

This is the collection of development methods within the BroadcastChannelSynchronizer interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createBroadcastChannelSynchronizer, within the synchronizer-broadcast-channel module.

createBroadcastChannelSynchronizer

The createBroadcastChannelSynchronizer function creates a BroadcastChannelSynchronizer object that can synchronize MergeableStore data to and from other MergeableStore instances via a browser's BroadcastChannel API.

createBroadcastChannelSynchronizer(
  store: MergeableStore,
  channelName: string,
  onSend?: Send,
  onReceive?: Receive,
  onIgnoredError?: (error: any) => void,
): BroadcastChannelSynchronizer
TypeDescription
storeMergeableStore

The MergeableStore to synchronize.

channelNamestring

The name of the channel to use.

onSend?Send

An optional handler for the messages that this Synchronizer sends. This is suitable for debugging synchronization issues in a development environment, since v5.1.

onReceive?Receive

An optional handler for the messages that this Synchronizer receives. This is suitable for debugging synchronization issues in a development environment, since v5.1.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Synchronizer would otherwise ignore when trying to synchronize data. This is suitable for debugging synchronization issues in a development environment.

returnsBroadcastChannelSynchronizer

A reference to the new BroadcastChannelSynchronizer object.

As well as providing a reference to the MergeableStore to persist, you must provide a channel name, used by all the browser tabs, workers, or contexts that need to synchronize together.

A final set of optional handlers can be provided to help debug sends, receives, and errors respectively.

Example

This example creates two BroadcastChannelSynchronizer objects to synchronize one MergeableStore to another.

import {createBroadcastChannelSynchronizer} from 'tinybase/synchronizers/synchronizer-broadcast-channel';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createBroadcastChannelSynchronizer(
  store1,
  'channelA',
);
const synchronizer2 = createBroadcastChannelSynchronizer(
  store2,
  'channelA',
);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
store2.setTables({pets: {felix: {species: 'cat'}}});

// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

synchronizer-local

The synchronizer-local module of the TinyBase project lets you synchronize MergeableStore data to and from other MergeableStore instances on the same local machine.

See also

Synchronization guide

Since

v5.0.0

Interfaces

There is one interface, LocalSynchronizer, within the synchronizer-local module.

LocalSynchronizer

The LocalSynchronizer interface represents a Synchronizer that lets you synchronize MergeableStore data to and from other MergeableStore instances on the same local machine.

You should use the createLocalSynchronizer function to create a LocalSynchronizer object.

Having no specialized methods, it is a synonym for the Synchronizer interface. This is also something of a showcase Synchronizer, rather than something you would use in a production environment. If you do need to synchronize two in-memory MergeableStore instances, you may prefer to use the merge function on either one of them instead of going to the effort of setting up this Synchronizer.

Since

v5.0.0

Getter methods

This is the collection of getter methods within the LocalSynchronizer interface. There is only one method, getStore.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): MergeableStore
returnsMergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

Listener methods

This is the collection of listener methods within the LocalSynchronizer interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<MergeableStoreOnly>): string
TypeDescription
listenerStatusListener<MergeableStoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the LocalSynchronizer interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<LocalSynchronizer>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<LocalSynchronizer>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the LocalSynchronizer interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<LocalSynchronizer>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<LocalSynchronizer>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<LocalSynchronizer>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<LocalSynchronizer>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the LocalSynchronizer interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<LocalSynchronizer>
returnsPromise<LocalSynchronizer>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<LocalSynchronizer>
returnsPromise<LocalSynchronizer>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Synchronization methods

This is the collection of synchronization methods within the LocalSynchronizer interface. There are only three synchronization methods, getSynchronizerStats, startSync, and stopSync.

getSynchronizerStats

The getSynchronizerStats method provides a set of statistics about the Synchronizer, and is used for debugging purposes.

getSynchronizerStats(): SynchronizerStats
returnsSynchronizerStats

A SynchronizerStats object containing Synchronizer send and receive statistics.

The SynchronizerStats object contains a count of the number of times the Synchronizer has sent and received messages.

The method is intended to be used during development to ensure your synchronization layer is acting as expected, for example.

Example

This example gets the send and receive statistics of two active Synchronizer instances.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
store2.setRow('pets', 'felix', {species: 'cat'});
// ...

console.log(synchronizer1.getSynchronizerStats());
// -> {receives: 4, sends: 5}
console.log(synchronizer2.getSynchronizerStats());
// -> {receives: 5, sends: 4}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

startSync

The startSync method is used to start the process of synchronization between this instance and another matching Synchronizer.

startSync(initialContent?: Content): Promise<LocalSynchronizer>
TypeDescription
initialContent?Content

An optional Content object used when no content is available from another other peer Synchronizer instances.

returnsPromise<LocalSynchronizer>

A Promise containing a reference to the Synchronizer object.

The underlying implementation of a Synchronizer is shared with the Persister framework, and so this startSync method is equivalent to starting both auto-loading (listening to sync messages from other active Synchronizer instances) and auto-saving (sending sync messages to it).

This method is asynchronous so you should you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them. A change in one becomes evident in the other.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

store2.setRow('pets', 'felix', {species: 'cat'});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them with default content. The default content from the first Synchronizer's startSync method ends up populated in both MergeableStore instances: by the time the second started, the first was available to synchronize with and its default was not needed.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync([{pets: {fido: {species: 'dog'}}}, {}]);
await synchronizer2.startSync([{pets: {felix: {species: 'cat'}}}, {}]);

// ...

console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

stopSync

The stopSync method is used to stop the process of synchronization between this instance and another matching Synchronizer.

stopSync(): this
returnsthis

A reference to the Synchronizer object.

The underlying implementation of a Synchronizer is shared with the Persister framework, and so this startSync method is equivalent to stopping both auto-loading (listening to sync messages from other active Synchronizer instances) and auto-saving (sending sync messages to them).

Example

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts - then stops - synchronizing them. Subsequent changes are not merged.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const synchronizer1 = createLocalSynchronizer(store1);
await synchronizer1.startSync();

const store2 = createMergeableStore();
const synchronizer2 = createLocalSynchronizer(store2);
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.stopSync();
synchronizer2.stopSync();

store1.setCell('pets', 'fido', 'color', 'brown');
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

Development methods

This is the collection of development methods within the LocalSynchronizer interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createLocalSynchronizer, within the synchronizer-local module.

createLocalSynchronizer

The createLocalSynchronizer function creates a LocalSynchronizer object that can synchronize MergeableStore data to and from other MergeableStore instances on the same local machine.

createLocalSynchronizer(
  store: MergeableStore,
  onSend?: Send,
  onReceive?: Receive,
  onIgnoredError?: (error: any) => void,
): LocalSynchronizer
TypeDescription
storeMergeableStore

The MergeableStore to synchronize.

onSend?Send

An optional handler for the messages that this Synchronizer sends. This is suitable for debugging synchronization issues in a development environment, since v5.1.

onReceive?Receive

An optional handler for the messages that this Synchronizer receives. This is suitable for debugging synchronization issues in a development environment, since v5.1.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Synchronizer would otherwise ignore when trying to synchronize data. This is suitable for debugging synchronization issues in a development environment.

returnsLocalSynchronizer

A reference to the new LocalSynchronizer object.

This is something of a showcase Synchronizer, rather than something you would use in a production environment. If you do need to synchronize two in-memory MergeableStore instances, you may prefer to use the merge function on either one of them instead of going to the effort of setting up this Synchronizer.

As well as providing a reference to the MergeableStore to persist, a final set of optional handlers can be provided to help debug sends, receives, and errors respectively.

Example

This example creates two LocalSynchronizer objects to synchronize one MergeableStore to another.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
store2.setTables({pets: {felix: {species: 'cat'}}});

// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

synchronizer-ws-client

The synchronizer-ws module of the TinyBase project lets you synchronize MergeableStore data to and from other MergeableStore instances via WebSockets facilitated by a server.

See also

Since

v5.0.0

Interfaces

There is one interface, WsSynchronizer, within the synchronizer-ws-client module.

WsSynchronizer

The WsSynchronizer interface represents a Synchronizer that lets you synchronize MergeableStore data to and from other MergeableStore instances via WebSockets facilitated by a server.

You should use the createWsSynchronizer function to create a WsSynchronizer object.

It is a minor extension to the Synchronizer interface and simply provides an extra getWebSocket method for accessing a reference to the WebSocket being used.

Since

v5.0.0

Getter methods

This is the collection of getter methods within the WsSynchronizer interface. There are only two getter methods, getStore and getWebSocket.

getStore

The getStore method returns a reference to the underlying Store or MergeableStore that is backing this Persister object.

getStore(): MergeableStore
returnsMergeableStore

A reference to the Store or MergeableStore.

Example

This example creates a Persister object against a newly-created Store and then gets its reference in order to update its data.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');
await persister.startAutoSave();

persister.getStore().setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

getWebSocket

The getWebSocket method returns a reference to the WebSocket being used for synchronization.

getWebSocket(): WebSocketType
returnsWebSocketType

The WebSocket reference.

Example

This example creates a server and WsSynchronizer object for a newly-created MergeableStore and then gets the WebSocket reference back out again.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8046}));

const store = createMergeableStore();
const webSocket = new WebSocket('ws://localhost:8046');
const synchronizer = await createWsSynchronizer(store, webSocket);

console.log(synchronizer.getWebSocket() == webSocket);
// -> true

synchronizer.destroy();
server.destroy();
Since

v5.0.0

Listener methods

This is the collection of listener methods within the WsSynchronizer interface. There are only two listener methods, addStatusListener and delListener.

addStatusListener

The addStatusListener method registers a listener function with the Persister that will be called whenever it starts or stops loading or saving.

addStatusListener(listener: StatusListener<MergeableStoreOnly>): string
TypeDescription
listenerStatusListener<MergeableStoreOnly>

The function that will be called whenever the Persister starts or stops loading or saving.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a StatusListener function, and will be called with a reference to the Persister and the new Status: 0 means now idle, 1 means now loading, and 2 means now saving.

Example

This example registers a listener that responds to changes in the state of the Persister.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((persister, status) => {
  console.log(
    `${persister.getStorageName()} persister status changed to ${status}`,
  );
});

await persister.load();
// -> 'pets persister status changed to 1'
// -> 'pets persister status changed to 0'
await persister.save();
// -> 'pets persister status changed to 2'
// -> 'pets persister status changed to 0'

persister.delListener(listenerId);
Since

v5.3.0

delListener

The delListener method removes a listener that was previously added to the Persister.

delListener(listenerId: string): this
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsthis

A reference to the Persister.

Use the Id returned by whichever method was used to add the listener. Note that the Persister may re-use this Id for future listeners added to it.

Example

This example registers a listener and then removes it.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

const listenerId = persister.addStatusListener((_persister, status) => {
  console.log(`Status changed to ${status}`);
});

await persister.load();
// -> `Status changed to 1`
// -> `Status changed to 0`

persister.delListener(listenerId);

await persister.load();
// -> undefined
// The listener is not called.
Since

v5.3.0

Lifecycle methods

This is the collection of lifecycle methods within the WsSynchronizer interface. There are only three lifecycle methods, destroy, getStatus, and schedule.

destroy

The destroy method should be called when this Persister object is no longer used.

destroy(): this
returnsthis

This guarantees that all of the listeners that the object registered with the underlying Store and storage are removed and it can be correctly garbage collected. It is equivalent to running the stopAutoLoad method and the stopAutoSave method in succession.

Example

This example creates a Store, associates a Persister object with it (that registers a TablesListener with the underlying Store), and then destroys it again, removing the listener.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

console.log(store.getListenerStats().transaction);
// -> 1

persister.destroy();

console.log(store.getListenerStats().transaction);
// -> 0
Since

v1.0.0

getStatus

The getStatus method lets you find out if the Persister is currently in the process of loading or saving content.

getStatus(): Status
returnsStatus

A value of type Status indicating whether the Persister is idle, loading, or saving.

It can only be doing one or the other (or neither) at any given time. The Status enum is returned, where 0 means idle, 1 means loading, and 2 means saving.

This method is only likely to be useful for Persister implementations that have asynchronous load or save operations. The status for synchronous persister media (such as browser local or session storage) will switch back to idle before you are able to query it.

Example

This example creates a Persister and queries its status.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.getStatus());
// -> 0
Since

v5.3.0

schedule

The schedule method allows you to queue up a series of asynchronous actions that must run in sequence during persistence.

schedule(actions: (() => Promise<any>)[]): Promise<WsSynchronizer<WebSocketType>>
TypeDescription
actions(() => Promise<any>)[]

One or many functions which will be scheduled, and which can be asynchronous.

returnsPromise<WsSynchronizer<WebSocketType>>

A reference to the Persister object.

For example, a database Persister may need to ensure that multiple asynchronous tasks to check and update the database schema are completed before data is written to it. Therefore it's most likely you will be using this method inside your setPersisted implementation.

Call this method to add a single asynchronous action, or a sequence of them in one call. This will also start to run the first task in the queue (which once complete will then run the next, and so on), and so this method itself is also asynchronous and returns a promise of the Persister.

Example

This example creates a custom Persister object against a newly-created Store and then sequences two tasks in order to update its data on a hypothetical remote system.

import {
  checkRemoteSystemIsReady,
  getDataFromRemoteSystem,
  sendDataToRemoteSystem,
} from 'custom-remote-handlers';
import {createCustomPersister} from 'tinybase/persisters';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createCustomPersister(
  store,
  async () => {
    // getPersisted
    return await getDataFromRemoteSystem();
  },
  async (getContent) => {
    // setPersisted
    await persister.schedule(
      async () => await checkRemoteSystemIsReady(),
      async () => await sendDataToRemoteSystem(getContent()),
    );
  },
  (listener) => setInterval(listener, 1000),
  (interval) => clearInterval(interval),
);
Since

v4.0.0

Load methods

This is the collection of load methods within the WsSynchronizer interface. There are 4 load methods in total.

isAutoLoading

The isAutoLoading method lets you find out if the Persister is currently automatically loading its content.

isAutoLoading(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoLoading.

Example

This example creates a Persister and queries whether it is autoLoading.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoLoading());
// -> false

await persister.startAutoLoad();
console.log(persister.isAutoLoading());
// -> true

await persister.stopAutoLoad();
console.log(persister.isAutoLoading());
// -> false
Since

v5.0.0

load

The load method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once.

load(initialContent?: Content): Promise<WsSynchronizer<WebSocketType>>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<WsSynchronizer<WebSocketType>>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates an empty Store, and loads data into it from the browser's session storage, which for the purposes of this example has been previously populated.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

sessionStorage.setItem('pets', '[{"pets":{"fido":{"species":"dog"}}},{}]');

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load();
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.clear();

This example creates an empty Store, and loads data into it from the browser's session storage, which is at first empty, so the optional parameter is used. The second time the load method is called, data has previously been persisted and instead, that is loaded.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.load([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
await persister.load({pets: {fido: {species: 'dog'}}});
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

sessionStorage.clear();
Since

v1.0.0

startAutoLoad

The startAutoLoad method gets persisted data from storage, and loads it into the Store with which the Persister is associated, once, and then continuously.

startAutoLoad(initialContent?: Content): Promise<WsSynchronizer<WebSocketType>>
TypeDescription
initialContent?Content

An optional Content object used when the underlying storage has not previously been populated.

returnsPromise<WsSynchronizer<WebSocketType>>

A Promise containing a reference to the Persister object.

The optional parameter allows you to specify what the initial content for the Store will be if there is nothing currently persisted or if the load fails (for example when the Persister is remote and the environment is offline). This allows you to fallback or instantiate a Store whether it's loading from previously persisted storage or being run for the first time.

This method first runs a single call to the load method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the underlying data (either through events or polling, depending on the storage type), automatically loading the data into the Store.

This method is asynchronous because it starts by making a single call to the asynchronous load method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates an empty Store, and loads data into it from the browser's session storage, which at first is empty (so the initialTables parameter is used). Subsequent changes to the underlying storage are then reflected in the Store (in this case through detection of StorageEvents from session storage changes made in another browser tab).

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad([{pets: {fido: {species: 'dog'}}}, {}]);
console.log(store.getTables());
// -> {pets: {fido: {species: 'dog'}}}

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})

// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

stopAutoLoad

The stopAutoLoad method stops the automatic loading of data from storage previously started with the startAutoLoad method.

stopAutoLoad(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically load, this method has no effect.

Example

This example creates an empty Store, and starts automatically loading data into it from the browser's session storage. Once the automatic loading is stopped, subsequent changes are not reflected in the Store.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');
await persister.startAutoLoad();

// In another browser tab:
sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}

persister.stopAutoLoad();

// In another browser tab:
sessionStorage.setItem(
  'pets',
  '[{"pets":{"felix":{"species":"cat"}}},{}]',
);
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...
console.log(store.getTables());
// -> {pets: {toto: {species: 'dog'}}}
// Storage change has not been automatically loaded.

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Save methods

This is the collection of save methods within the WsSynchronizer interface. There are 4 save methods in total.

isAutoSaving

The isAutoSaving method lets you find out if the Persister is currently automatically saving its content.

isAutoSaving(): boolean
returnsboolean

A boolean indicating whether the Persister is currently autoSaving.

Example

This example creates a Persister and queries whether it is autoSaving.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const persister = createSessionPersister(createStore(), 'pets');

console.log(persister.isAutoSaving());
// -> false

await persister.startAutoSave();
console.log(persister.isAutoSaving());
// -> true

await persister.stopAutoSave();
console.log(persister.isAutoSaving());
// -> false
Since

v5.0.0

save

The save method takes data from the Store with which the Persister is associated and persists it into storage, once.

save(): Promise<WsSynchronizer<WebSocketType>>
returnsPromise<WsSynchronizer<WebSocketType>>

A Promise containing a reference to the Persister object.

This method is asynchronous because the persisted data may be on a remote machine or a filesystem. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.save();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

startAutoSave

The save method takes data from the Store with which the Persister is associated and persists it into storage, once, and then continuously.

startAutoSave(): Promise<WsSynchronizer<WebSocketType>>
returnsPromise<WsSynchronizer<WebSocketType>>

A Promise containing a reference to the Persister object.

This method first runs a single call to the save method to ensure the data is in sync with the persisted storage. It then continues to watch for changes to the Store, automatically saving the data to storage.

This method is asynchronous because it starts by making a single call to the asynchronous save method. Even for those storage types that are synchronous (like browser storage) it is still recommended that you await calls to this method or handle the return type natively as a Promise.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');

await persister.startAutoSave();
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"fido":{"species":"dog"}}},{}]'

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

sessionStorage.clear();
Since

v1.0.0

stopAutoSave

The stopAutoSave method stops the automatic save of data to storage previously started with the startAutoSave method.

stopAutoSave(): this
returnsthis

A reference to the Persister object.

If the Persister is not currently set to automatically save, this method has no effect.

Example

This example creates a Store with some data, and saves into the browser's session storage. Subsequent changes to the Store are then automatically saved to the underlying storage. Once the automatic saving is stopped, subsequent changes are not reflected.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const persister = createSessionPersister(store, 'pets');
await persister.startAutoSave();

store.setTables({pets: {toto: {species: 'dog'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'

persister.stopAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...
console.log(sessionStorage.getItem('pets'));
// -> '[{"pets":{"toto":{"species":"dog"}}},{}]'
// Store change has not been automatically saved.

sessionStorage.clear();
Since

v1.0.0

Synchronization methods

This is the collection of synchronization methods within the WsSynchronizer interface. There are only three synchronization methods, getSynchronizerStats, startSync, and stopSync.

getSynchronizerStats

The getSynchronizerStats method provides a set of statistics about the Synchronizer, and is used for debugging purposes.

getSynchronizerStats(): SynchronizerStats
returnsSynchronizerStats

A SynchronizerStats object containing Synchronizer send and receive statistics.

The SynchronizerStats object contains a count of the number of times the Synchronizer has sent and received messages.

The method is intended to be used during development to ensure your synchronization layer is acting as expected, for example.

Example

This example gets the send and receive statistics of two active Synchronizer instances.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
store2.setRow('pets', 'felix', {species: 'cat'});
// ...

console.log(synchronizer1.getSynchronizerStats());
// -> {receives: 4, sends: 5}
console.log(synchronizer2.getSynchronizerStats());
// -> {receives: 5, sends: 4}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

startSync

The startSync method is used to start the process of synchronization between this instance and another matching Synchronizer.

startSync(initialContent?: Content): Promise<WsSynchronizer<WebSocketType>>
TypeDescription
initialContent?Content

An optional Content object used when no content is available from another other peer Synchronizer instances.

returnsPromise<WsSynchronizer<WebSocketType>>

A Promise containing a reference to the Synchronizer object.

The underlying implementation of a Synchronizer is shared with the Persister framework, and so this startSync method is equivalent to starting both auto-loading (listening to sync messages from other active Synchronizer instances) and auto-saving (sending sync messages to it).

This method is asynchronous so you should you await calls to this method or handle the return type natively as a Promise.

Examples

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them. A change in one becomes evident in the other.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

store2.setRow('pets', 'felix', {species: 'cat'});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts synchronizing them with default content. The default content from the first Synchronizer's startSync method ends up populated in both MergeableStore instances: by the time the second started, the first was available to synchronize with and its default was not needed.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = createLocalSynchronizer(store1);
const synchronizer2 = createLocalSynchronizer(store2);

await synchronizer1.startSync([{pets: {fido: {species: 'dog'}}}, {}]);
await synchronizer2.startSync([{pets: {felix: {species: 'cat'}}}, {}]);

// ...

console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

stopSync

The stopSync method is used to stop the process of synchronization between this instance and another matching Synchronizer.

stopSync(): this
returnsthis

A reference to the Synchronizer object.

The underlying implementation of a Synchronizer is shared with the Persister framework, and so this startSync method is equivalent to stopping both auto-loading (listening to sync messages from other active Synchronizer instances) and auto-saving (sending sync messages to them).

Example

This example creates two empty MergeableStore objects, creates a LocalSynchronizer for each, and starts - then stops - synchronizing them. Subsequent changes are not merged.

import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';

const store1 = createMergeableStore();
const synchronizer1 = createLocalSynchronizer(store1);
await synchronizer1.startSync();

const store2 = createMergeableStore();
const synchronizer2 = createLocalSynchronizer(store2);
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.stopSync();
synchronizer2.stopSync();

store1.setCell('pets', 'fido', 'color', 'brown');
// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog', color: 'brown'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}}}

synchronizer1.destroy();
synchronizer2.destroy();
Since

v5.0.0

Development methods

This is the collection of development methods within the WsSynchronizer interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the Persister, and is used for debugging purposes.

getStats(): PersisterStats
returnsPersisterStats

A PersisterStats object containing Persister load and save statistics.

The PersisterStats object contains a count of the number of times the Persister has loaded and saved data.

The method is intended to be used during development to ensure your persistence layer is acting as expected, for example.

Example

This example gets the load and save statistics of a Persister object. Remember that the startAutoLoad method invokes an explicit load when it starts, and the startAutoSave method invokes an explicit save when it starts - so those numbers are included in addition to the loads and saves invoked by changes to the Store and to the underlying storage.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const store = createStore();
const persister = createSessionPersister(store, 'pets');

await persister.startAutoLoad({pets: {fido: {species: 'dog'}}});
await persister.startAutoSave();

store.setTables({pets: {felix: {species: 'cat'}}});
// ...

sessionStorage.setItem('pets', '[{"pets":{"toto":{"species":"dog"}}},{}]');
// -> StorageEvent('storage', {storageArea: sessionStorage, key: 'pets'})
// ...

console.log(persister.getStats());
// -> {loads: 2, saves: 2}

persister.destroy();
sessionStorage.clear();
Since

v1.0.0

Functions

There is one function, createWsSynchronizer, within the synchronizer-ws-client module.

createWsSynchronizer

The createWsSynchronizer function creates a WsSynchronizer object that can synchronize MergeableStore data to and from other MergeableStore instances via WebSockets facilitated by a WsServer.

createWsSynchronizer<WebSocketType>(
  store: MergeableStore,
  webSocket: WebSocketType,
  requestTimeoutSeconds?: number,
  onSend?: Send,
  onReceive?: Receive,
  onIgnoredError?: (error: any) => void,
): Promise<WsSynchronizer<WebSocketType>>
TypeDescription
storeMergeableStore

The MergeableStore to synchronize.

webSocketWebSocketType

The WebSocket to send synchronization messages over.

requestTimeoutSeconds?number

An optional time in seconds that the Synchronizer will wait for responses to request messages, defaulting to 1.

onSend?Send

An optional handler for the messages that this Synchronizer sends. This is suitable for debugging synchronization issues in a development environment, since v5.1.

onReceive?Receive

An optional handler for the messages that this Synchronizer receives. This is suitable for debugging synchronization issues in a development environment, since v5.1.

onIgnoredError?(error: any) => void

An optional handler for the errors that the Synchronizer would otherwise ignore when trying to synchronize data. This is suitable for debugging synchronization issues in a development environment.

returnsPromise<WsSynchronizer<WebSocketType>>

A reference to the new WsSynchronizer object.

As well as providing a reference to the MergeableStore to persist, you must provide a configured WebSocket to send synchronization messages over.

Instead of the raw browser implementation of WebSocket, you may prefer to use the Reconnecting WebSocket wrapper so that if a client goes offline, it can easily re-establish a connection when it comes back online. Its API is compatible with this Synchronizer.

You can indicate how long the Synchronizer will wait for responses to message requests before timing out. A final set of optional handlers can be provided to help debug sends, receives, and errors respectively.

This method is asynchronous because it will await the websocket's connection to the server. You will need to await a call to this function or handle the return type natively as a Promise.

Example

This example creates two WsSynchronizer objects to synchronize one MergeableStore to another via a server.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8047}));

const store1 = createMergeableStore();
const store2 = createMergeableStore();

const synchronizer1 = await createWsSynchronizer(
  store1,
  new WebSocket('ws://localhost:8047'),
);
const synchronizer2 = await createWsSynchronizer(
  store2,
  new WebSocket('ws://localhost:8047'),
);

await synchronizer1.startSync();
await synchronizer2.startSync();

store1.setTables({pets: {fido: {species: 'dog'}}});
store2.setTables({pets: {felix: {species: 'cat'}}});

// ...
console.log(store1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}
console.log(store2.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();
server.destroy();
Since

v5.0.0

Type Aliases

There is one type alias, WebSocketTypes, within the synchronizer-ws-client module.

WebSocketTypes

The WebSocketTypes type represents the valid types of WebSocket that can be used with the WsSynchronizer.

WebSocket | WsWebSocket

This includes the browser-native WebSocket type, as well as the WebSocket type from the well-known ws package (such that the Synchronizer can be used in a server environment).

Since

v5.0.0

synchronizer-ws-server

The synchronizer-ws-server module of the TinyBase project lets you create a server that facilitates synchronization between clients.

See also

Since

v5.0.0

Interfaces

There is one interface, WsServer, within the synchronizer-ws-server module.

WsServer

The WsServer interface represents an object that facilitates synchronization between clients that are using WsSynchronizer instances.

You should use the createWsServer function to create a WsServer object.

Since

v5.0.0

Getter methods

This is the collection of getter methods within the WsServer interface. There are 4 getter methods in total.

destroy

The destroy method provides a way to clean up the server at the end of its use.

destroy(): void

This closes the underlying WebSocketServer that was provided when the WsServer was created.

Example

This example creates a WsServer and then destroys it again, closing the underlying WebSocketServer.

import {WebSocketServer} from 'ws';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';

const webSocketServer = new WebSocketServer({port: 8047});
webSocketServer.on('close', () => {
  console.log('WebSocketServer closed');
});
const server = createWsServer(webSocketServer);

server.destroy();
// ...
// -> 'WebSocketServer closed'
Since

v5.0.0

getClientIds

The getClientIds method method returns the active clients that the WsServer is handling for a given path.

getClientIds(pathId: string): Ids
TypeDescription
pathIdstring
returnsIds

An array of the clients connected to the given path.

Example

This example creates a WsServer, sets some clients up to connect to it, and then gets the number of clients on the given paths. (The client Ids themselves are unique, based on the sec-websocket-key header.)

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8047}));

const synchronizer1 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomA'),
);
const synchronizer2 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomA'),
);
const synchronizer3 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomB'),
);

console.log(server.getClientIds('roomA').length);
// -> 2
console.log(server.getClientIds('roomB').length);
// -> 1

synchronizer3.destroy();
// ...
console.log(server.getClientIds('roomB').length);
// -> 0

synchronizer1.destroy();
synchronizer2.destroy();
server.destroy();
Since

v5.0.0

getPathIds

The getPathIds method returns the active paths that the WsServer is handling.

getPathIds(): Ids
returnsIds

An array of the paths that have clients connected to them.

These will be all the paths that have at least one active client connected to them.

Example

This example creates a WsServer, sets some clients up to connect to it, and then enumerates the paths being used.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8047}));
console.log(server.getPathIds());
// -> []

const synchronizer1 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomA'),
);
const synchronizer2 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomA'),
);
const synchronizer3 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomB'),
);

console.log(server.getPathIds());
// -> ['roomA', 'roomB']

synchronizer3.destroy();
// ...
console.log(server.getPathIds());
// -> ['roomA']

synchronizer1.destroy();
synchronizer2.destroy();
server.destroy();
Since

v5.0.0

getWebSocketServer

The getWebSocketServer method returns a reference to the WebSocketServer being used for this WsServer.

getWebSocketServer(): WebSocketServer
returnsWebSocketServer

The WebSocketServer reference.

Example

This example creates a WsServer and then gets the WebSocketServer reference back out again.

import {WebSocketServer} from 'ws';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';

const webSocketServer = new WebSocketServer({port: 8047});
const server = createWsServer(webSocketServer);

console.log(server.getWebSocketServer() == webSocketServer);
// -> true

server.destroy();
Since

v5.0.0

Listener methods

This is the collection of listener methods within the WsServer interface. There are only three listener methods, addClientIdsListener, addPathIdsListener, and delListener.

addClientIdsListener

The addClientIdsListener method registers a listener function with the WsServer that will be called whenever there is a change in the clients connected to a path that a WsServer is handling.

addClientIdsListener(
  pathId: IdOrNull,
  listener: ClientIdsListener,
): string
TypeDescription
pathIdIdOrNull

The path to listen to, or null as a wildcard.

listenerClientIdsListener

The function that will be called whenever the client Ids on a path handled by the WsServer change.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a ClientIdsListener function, and will be called with a reference to the WsServer, the Id of the path that the client joined or left, and a callback you can use to get information about the change.

You can either listen to a single path (by specifying its Id as the method's first parameter) or changes to any path (by providing a null wildcard).

Examples

This example creates a WsServer, and listens to changes to the clients connecting to and disconnecting from a specific path.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8047}));
const listenerId = server.addClientIdsListener(
  'roomA',
  (server, pathId) => {
    console.log(
      `${server.getClientIds(pathId).length} client(s) in roomA`,
    );
  },
);

const synchronizer1 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomA'),
);
// -> '1 client(s) in roomA'

const synchronizer2 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomB'),
);
// The listener is not called.

synchronizer1.destroy();
// ...
// -> '0 client(s) in roomA'

synchronizer2.destroy();

server.delListener(listenerId);
server.destroy();

This example creates a WsServer, and listens to changes to the clients connecting to and disconnecting from any path.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8047}));
const listenerId = server.addClientIdsListener(null, (server, pathId) => {
  console.log(
    `${server.getClientIds(pathId).length} client(s) in ${pathId}`,
  );
});

const synchronizer1 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomA'),
);
// -> '1 client(s) in roomA'

const synchronizer2 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomB'),
);
// -> '1 client(s) in roomB'

synchronizer1.destroy();
// ...
// -> '0 client(s) in roomA'

synchronizer2.destroy();
// ...
// -> '0 client(s) in roomB'

server.delListener(listenerId);
server.destroy();
Since

v5.0.0

addPathIdsListener

The addPathIdsListener method registers a listener function with the WsServer that will be called whenever there is a change in the active paths that a WsServer is handling.

addPathIdsListener(listener: PathIdsListener): string
TypeDescription
listenerPathIdsListener

The function that will be called whenever the path Ids handled by the WsServer change.

returnsstring

A unique Id for the listener that can later be used to remove it.

The provided listener is a PathIdsListener function, and will be called with a reference to the WsServer and a callback you can use to get information about the change.

Example

This example creates a WsServer, and listens to changes to the active paths when clients connect to and disconnect from it.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8047}));
const listenerId = server.addPathIdsListener(
  (server, pathId, addedOrRemoved) => {
    console.log(pathId + (addedOrRemoved == 1 ? ' added' : ' removed'));
    console.log(server.getPathIds());
  },
);

const synchronizer1 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomA'),
);
// -> 'roomA added'
// -> ['roomA']

const synchronizer2 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomB'),
);
// -> 'roomB added'
// -> ['roomA', 'roomB']

synchronizer1.destroy();
// ...
// -> 'roomA removed'
// -> ['roomB']

synchronizer2.destroy();
// ...
// -> 'roomB removed'
// -> []

server.delListener(listenerId);
server.destroy();
Since

v5.0.0

delListener

The delListener method removes a listener that was previously added to the WsServer.

delListener(listenerId: string): WsServer
TypeDescription
listenerIdstring

The Id of the listener to remove.

returnsWsServer

A reference to the WsServer.

Use the Id returned by whichever method was used to add the listener. Note that the WsServer may re-use this Id for future listeners added to it.

Example

This example registers a listener to a WsServer and then removes it.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8047}));
const listenerId = server.addPathIdsListener(() => {
  console.log('Paths changed');
});

const synchronizer = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047/roomA'),
);
// -> 'Paths changed'

server.delListener(listenerId);

synchronizer.destroy();
// -> undefined
// The listener is not called.

server.destroy();
Since

v5.0.0

Development methods

This is the collection of development methods within the WsServer interface. There is only one method, getStats.

getStats

The getStats method provides a set of statistics about the WsServer, and is used for debugging purposes.

getStats(): WsServerStats
returnsWsServerStats

A WsServerStats object containing statistics.

The WsServerStats object contains the number of paths and clients that are active on the WsServer and is intended to be used during development.

Example

This example creates a WsServer that facilitates some synchronization, demonstrating the statistics of the paths and clients handled as a result.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server = createWsServer(new WebSocketServer({port: 8047}));

const synchronizer1 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047'),
);
const synchronizer2 = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047'),
);

console.log(server.getStats());
// -> {paths: 1, clients: 2}

synchronizer1.destroy();
synchronizer2.destroy();
server.destroy();
Since

v5.0.0

Functions

There is one function, createWsServer, within the synchronizer-ws-server module.

createWsServer

The createWsServer function creates a WsServer that facilitates synchronization between clients that are using WsSynchronizer instances.

createWsServer<PathPersister>(
  webSocketServer: WebSocketServer,
  createPersisterForPath?: (pathId: string) => undefined | PathPersister | [PathPersister, (store: MergeableStore) => void] | Promise<PathPersister> | Promise<[PathPersister, (store: MergeableStore) => void]>,
): WsServer
TypeDescription
webSocketServerWebSocketServer

A WebSocketServer object from your server environment.

createPersisterForPath?(pathId: string) => undefined | PathPersister | [PathPersister, (store: MergeableStore) => void] | Promise<PathPersister> | Promise<[PathPersister, (store: MergeableStore) => void]>

An optional function that will create a Persister to synchronize with the clients on a given path (or a two-item array of Persister and callback that lets you handle data after persistence has started).

returnsWsServer

A reference to the new WsServer object.

This should be run in a server environment, and you must pass in a configured WebSocketServer object in order to create it.

If you want your server to persist data itself, you can use the optional second parameter of this function, which allows you to create a Persister for a new path - whenever a new path is accessed by a client. This Persister will only exist when there are active clients on that particular path. The creation callback can be asynchronous.

You are responsible for creating a MergeableStore to pass to this Persister, but starting and stopping its automatic saving and loading is taken care of by the WsServer. As a result, the server MergeableStore will be kept in sync with the clients on that path, and in turn with whatever persistence layer you have configured. See the example below.

It is not safe to add or manipulate data in the MergeableStore during the createPersisterForPath function, since changes will probably be overwritten when the Persister starts. If you wish to modify data - or upgrade a schema, for example - you can have that function instead return an array containing the Persister and a callback that takes the MergeableStore. That callback will get called after the Persister has started, and is an appropriate place to manipulate data in a way that will be transmitted to clients. Again, see the example below.

Examples

This example creates a WsServer that synchronizes two clients on a shared path.

import {WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

// Server
const server = createWsServer(new WebSocketServer({port: 8047}));

// Client 1
const clientStore1 = createMergeableStore();
clientStore1.setCell('pets', 'fido', 'species', 'dog');
const synchronizer1 = await createWsSynchronizer(
  clientStore1,
  new WebSocket('ws://localhost:8047/petShop'),
);
await synchronizer1.startSync();
// ...

// Client 2
const clientStore2 = createMergeableStore();
clientStore2.setCell('pets', 'felix', 'species', 'cat');
const synchronizer2 = await createWsSynchronizer(
  clientStore2,
  new WebSocket('ws://localhost:8047/petShop'),
);
await synchronizer2.startSync();
// ...

console.log(clientStore1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

console.log(clientStore2.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();
server.destroy();

This longer example creates a WsServer that persists a MergeableStore to file that is synchronized with two clients on a shared path. Later, when a third client connects, it picks up the data the previous two were using.

import {WebSocketServer} from 'ws';
import {createFilePersister} from 'tinybase/persisters/persister-file';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';
import {rmSync} from 'fs';

// Server
const server = createWsServer(
  new WebSocketServer({port: 8047}),
  (pathId) =>
    createFilePersister(
      createMergeableStore(),
      pathId.replace(/[^a-zA-Z0-9]/g, '-') + '.json',
    ),
);

// Client 1
const clientStore1 = createMergeableStore();
clientStore1.setCell('pets', 'fido', 'species', 'dog');
const synchronizer1 = await createWsSynchronizer(
  clientStore1,
  new WebSocket('ws://localhost:8047/petShop'),
);
await synchronizer1.startSync();
// ...

// Client 2
const clientStore2 = createMergeableStore();
clientStore2.setCell('pets', 'felix', 'species', 'cat');
const synchronizer2 = await createWsSynchronizer(
  clientStore2,
  new WebSocket('ws://localhost:8047/petShop'),
);
await synchronizer2.startSync();
// ...

console.log(clientStore1.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

console.log(clientStore2.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer1.destroy();
synchronizer2.destroy();

// ...
// Client 3 connects later
const clientStore3 = createMergeableStore();
const synchronizer3 = await createWsSynchronizer(
  clientStore3,
  new WebSocket('ws://localhost:8047/petShop'),
);
await synchronizer3.startSync();
// ...

console.log(clientStore3.getTables());
// -> {pets: {fido: {species: 'dog'}, felix: {species: 'cat'}}}

synchronizer3.destroy();
server.destroy();

// Remove file for the purposes of this demo.
rmSync('petShop.json');

This example creates a WsServer that persists a MergeableStore to file that is synchronized with two clients on a shared path, but also which updates its data once synchronization has started.

import {WebSocketServer} from 'ws';
import {createFilePersister} from 'tinybase/persisters/persister-file';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';
import {rmSync} from 'fs';

// Server
const server = createWsServer(
  new WebSocketServer({port: 8047}),
  (pathId) => [
    createFilePersister(
      createMergeableStore(),
      pathId.replace(/[^a-zA-Z0-9]/g, '-') + '.json',
    ),
    (store) => store.setValue('pathId', pathId),
  ],
);

const clientStore = createMergeableStore();
clientStore.setCell('pets', 'fido', 'species', 'dog');
const synchronizer = await createWsSynchronizer(
  clientStore,
  new WebSocket('ws://localhost:8047/petShop'),
);
await synchronizer.startSync();
// ...

console.log(clientStore.getContent());
// -> [{pets: {fido: {species: 'dog'}}}, {"pathId": "petShop"}]

synchronizer.destroy();
server.destroy();

// Remove file for the purposes of this demo.
rmSync('petShop.json');

This example creates a WsServer with a custom listener that displays information about the address of the client that connects to it.

import {WebSocket, WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

// On the server:
const webSocketServer = new WebSocketServer({port: 8047});
webSocketServer.on('connection', (_, request) => {
  console.log('Client address: ' + request.socket.remoteAddress);
});
const server = createWsServer(webSocketServer);

// On a client:
const synchronizer = await createWsSynchronizer(
  createMergeableStore(),
  new WebSocket('ws://localhost:8047'),
);
// -> 'Client address: ::1'

synchronizer.destroy();
server.destroy();
Since

v5.0.0

Type Aliases

These are the type aliases within the synchronizer-ws-server module.

Listener type aliases

This is the collection of listener type aliases within the synchronizer-ws-server module. There are only two listener type aliases, ClientIdsListener and PathIdsListener.

ClientIdsListener

The ClientIdsListener type describes a function that is used to listen to clients joining and leaving the active paths that a WsServer is handling.

(
  wsServer: WsServer,
  pathId: Id,
  clientId: Id,
  addedOrRemoved: IdAddedOrRemoved,
): void
TypeDescription
wsServerWsServer

A reference to the WsServer.

pathIdId

The path that the client joined or left.

clientIdId

The Id of the client being added or removed.

addedOrRemovedIdAddedOrRemoved

Whether the client was added (1) or removed (-1).

returnsvoid

This has no return value.

A WsServer listens to any path, allowing an app to have the concept of distinct 'rooms' that only certain clients are participating in. As soon as a new client connects to a path, this listener will be called with the Id of the path, the Id of the new client, and an addedOrRemoved value of 1.

When the client disconnects from a path, it will be called again with the Id of the path, the Id of the leaving client, and an addedOrRemoved value of -1.

A ClientIdsListener is provided when using the addClientIdsListener method. See that method for specific examples.

Since

v5.0.3

PathIdsListener

The PathIdsListener type describes a function that is used to listen to changes of active paths that a WsServer is handling.

(
  wsServer: WsServer,
  pathId: Id,
  addedOrRemoved: IdAddedOrRemoved,
): void
TypeDescription
wsServerWsServer

A reference to the WsServer.

pathIdId

The Id of the path being added or removed.

addedOrRemovedIdAddedOrRemoved

Whether the path was added (1) or removed (-1).

returnsvoid

This has no return value.

A WsServer listens to any path, allowing an app to have the concept of distinct 'rooms' that only certain clients are participating in. As soon as a single client connects to a new path, this listener will be called with the Id of the new path and an addedOrRemoved value of 1.

When the final client disconnects from a path, it will be called again with the Id of the deactivated path and an addedOrRemoved value of -1.

A PathIdsListener is provided when using the addPathIdsListener method. See that method for specific examples.

Since

v5.0.3

Development type aliases

This is the collection of development type aliases within the synchronizer-ws-server module. There is only one type alias, WsServerStats.

WsServerStats

The WsServerStats type describes the number of paths and clients that are active on the WsServer.

{
  paths: number;
  clients: number;
}
TypeDescription
pathsnumber

The number of paths currently being served by the WsServer.

clientsnumber

The number of clients currently being served by the WsServer.

A WsServerStats object is returned from the getStats method.

Since

v5.0.0

tools

The tools module of the TinyBase project provides utilities for working with TinyBase during development.

This module is not intended to be directly used at runtime in a production environment.

Since

v2.2.0

Interfaces

There is one interface, Tools, within the tools module.

Tools

A Tools object lets you run various utilities on, and get certain information about, Store objects in development.

See also

Developer Tools guides

Since

v2.2.0

Getter methods

This is the collection of getter methods within the Tools interface. There is only one method, getStore.

getStore

The getStore method returns a reference to the underlying Store that is backing this Tools object.

getStore(): Store
returnsStore

A reference to the Store.

Example

This example creates a Tools object against a newly-created Store and then gets its reference in order to update its data.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const tools = createTools(createStore());
tools.getStore().setCell('species', 'dog', 'price', 5);
console.log(tools.getStoreStats().totalCells);
// -> 1
Since

v3.0.0

Modelling methods

This is the collection of modelling methods within the Tools interface. There are 4 modelling methods in total.

getPrettyStoreApi

The getPrettyStoreApi method attempts to return prettified code-generated .d.ts and .ts(x) files that describe the schema of a Store and React bindings (since v3.1) in an ORM style.

getPrettyStoreApi(storeName: string): Promise<[string, string, string, string]>
TypeDescription
storeNamestring

The name you want to provide to the generated Store, which should also be used to save the .d.ts, .ts, and .tsx files.

returnsPromise<[string, string, string, string]>

A set of four strings representing the contents of the .d.ts, .ts, and .tsx files for the generated Store and React modules.

This is simply a wrapper around the getStoreApi method that attempts to invoke the prettier module (which it hopes you have installed) to format the generated code. If prettier is not present, the output will resemble that of the underlying getStoreApi method.

The method is asynchronous, so you should use the await keyword or handle the results as a promise.

The method takes a single argument which represents the name you want the generated store object to have in code. You are expected to save the four files yourself, as the following:

  • [storeName].d.ts as the main definition,
  • [storeName].ts as the main library,
  • [storeName]-ui-react.d.ts as the ui-react module definition,
  • [storeName]-ui-react.tsx as the ui-react module library.

Also you should save these alongside each other so that the .ts(x) files can import types from the .d.ts files.

See the documentation for the getStoreApi method for details of the content of the generated files.

Examples

This example creates a Tools object and generates code for a Store that already has a TablesSchema.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore().setTablesSchema({
  pets: {
    price: {type: 'number'},
  },
});
const [dTs, ts, _uiReactDTs, _uiReactTsx] =
  await createTools(store).getPrettyStoreApi('shop');

const dTsLines = dTs.split('\n');
console.log(dTsLines[15]);
// -> `export type Tables = {pets?: {[rowId: Id]: {price?: number}}};`

const tsLines = ts.split('\n');
console.log(tsLines[89]);
// -> '    hasPetsTable: (): boolean => store.hasTable(PETS),'

This example creates a Tools object and generates code for a Store that doesn't already have a TablesSchema.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore().setTable('pets', {
  fido: {price: 5},
  felix: {price: 4},
});
const [dTs, ts, _uiReactDTs, _uiReactTsx] =
  await createTools(store).getPrettyStoreApi('shop');

const dTsLines = dTs.split('\n');
console.log(dTsLines[15]);
// -> 'export type Tables = {pets?: {[rowId: Id]: {price: number}}};'

const tsLines = ts.split('\n');
console.log(tsLines[91]);
// -> '    hasPetsTable: (): boolean => store.hasTable(PETS),'
Since

v2.2.0

getStoreApi

The getStoreApi method returns code-generated .d.ts and .ts(x) files that describe the schema of a Store and React bindings (since v3.1) in an ORM style.

getStoreApi(storeName: string): [string, string, string, string]
TypeDescription
storeNamestring

The name you want to provide to the generated Store, which should also be used to save the .d.ts, .ts, and .tsx files.

returns[string, string, string, string]

A set of four strings representing the contents of the .d.ts, .ts, and .tsx files for the generated Store and React modules.

If the Store does not already have an explicit TablesSchema or ValuesSchema associated with it, the data in the Store will be scanned to attempt to infer new schemas. The method returns four strings (which should be saved as files) though if no schema can be inferred, the strings will be empty.

The method takes a single argument which represents the name you want the generated store object to have in code. You are expected to save the four files yourself, as the following:

  • [storeName].d.ts as the main definition,
  • [storeName].ts as the main library,
  • [storeName]-ui-react.d.ts as the ui-react module definition,
  • [storeName]-ui-react.tsx as the ui-react module library.

Also you should save these alongside each other so that the .ts(x) files can import types from the .d.ts files.

The .d.ts and .ts(x) files that are generated are designed to resemble the main TinyBase Store and React binding files, but provide named types and methods that describe the domain of the schema in the Store.

For example, from a Store that has a pets Table, you will get methods like getPetsTable, types like PetsRow, and hooks and components that are more specific versions of the underlying getTable method or the Row type, and so on. For example:

Store typeEquivalent generated type
Table[Table]Table
Row[Table]Row
(Cell) Id[Table]CellId
CellCallback[Table]CellCallback
......
Store methodEquivalent generated method
setTableset[Table]Table
hasRowhas[Table]Row
getCellget[Table][Cell]Cell
......

Equivalent to the TinyBase createStore function, a create[StoreName] function will also be created. This acts as the main entry point to the generated implementation.

Each method is refined correctly to take, or return, the types specified by the schema. For example, if the pets Table has a numeric price Cell in the schema, the getPetsPriceCell method will be typed to return a number.

The tables above include just a sample of the generated output. For the full set of methods and types generated by this method, inspect the output directly.

Examples

This example creates a Tools object and generates code for a Store that already has a TablesSchema.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore().setTablesSchema({
  pets: {
    price: {type: 'number'},
  },
});
const [dTs, ts, _uiReactDTs, _uiReactTsx] =
  createTools(store).getStoreApi('shop');

const dTsLines = dTs.split('\n');
console.log(dTsLines[3]);
// -> `export type Tables = {'pets'?: {[rowId: Id]: {'price'?: number}}};`

const tsLines = ts.split('\n');
console.log(tsLines[39]);
// -> 'getPetsTable: (): PetsTable => store.getTable(PETS) as PetsTable,'

This example creates a Tools object and generates code for a Store that doesn't already have a TablesSchema.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore().setTable('pets', {
  fido: {price: 5},
  felix: {price: 4},
});
const [dTs, ts, _uiReactDTs, _uiReactTsx] =
  createTools(store).getStoreApi('shop');

const dTsLines = dTs.split('\n');
console.log(dTsLines[3]);
// -> `export type Tables = {'pets'?: {[rowId: Id]: {'price': number}}};`

const tsLines = ts.split('\n');
console.log(tsLines[41]);
// -> 'getPetsTable: (): PetsTable => store.getTable(PETS) as PetsTable,'
Since

v2.2.0

getStoreTablesSchema

The getStoreTablesSchema method returns the TablesSchema of the Store as an object.

getStoreTablesSchema(): TablesSchema
returnsTablesSchema

A TablesSchema object for the Store.

If the Store does not already have an explicit TablesSchema associated with it, the data in the Store will be scanned to attempt to infer a new TablesSchema.

To be successful, this requires all the values of a given Cell across a Table object's Row objects to have a consistent type. If a given Cell Id appears in every Row, then a default for that Cell is specified in the TablesSchema, based on the most common value found.

Examples

This example creates a Tools object and gets the schema of a Store that already has a TablesSchema.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore().setTablesSchema({
  pets: {
    species: {type: 'string'},
    color: {type: 'string'},
  },
  species: {
    price: {type: 'number'},
  },
});
const schema = createTools(store).getStoreTablesSchema();
console.log(schema.pets);
// -> {species: {type: 'string'}, color: {type: 'string'}}

This example creates a Tools object and infers the schema of a Store that doesn't already have a TablesSchema.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  })
  .setTable('species', {
    dog: {price: 5, barks: true},
    cat: {price: 4, purrs: true},
  });
const schema = createTools(store).getStoreTablesSchema();
console.log(schema.pets.species);
// -> {type: 'string', default: 'dog'}
console.log(schema.pets.color);
// -> {type: 'string', default: 'black'}
console.log(schema.species.price);
// -> {type: 'number', default: 5}
console.log(schema.species.barks);
// -> {type: 'boolean'}
console.log(schema.species.purrs);
// -> {type: 'boolean'}
Since

v3.0.0

getStoreValuesSchema

The getStoreValuesSchema method returns the ValuesSchema of the Store as an object.

getStoreValuesSchema(): ValuesSchema
returnsValuesSchema

A ValuesSchema object for the Store.

If the Store does not already have an explicit ValuesSchema associated with it, the data in the Store will be scanned to infer a new ValuesSchema, based on the types of the Values present. Note that, unlike the inference of Cell values in the TablesSchema, it is not able to determine whether a Value should have a default or not.

Examples

This example creates a Tools object and gets the schema of a Store that already has a ValuesSchema.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore().setValuesSchema({
  open: {type: 'boolean', default: true},
  employees: {type: 'number'},
});

const schema = createTools(store).getStoreValuesSchema();
console.log(schema);
// -> {open: {type: 'boolean', default: true}, employees: {type: 'number'}}

This example creates a Tools object and infers the schema of a Store that doesn't already have a ValuesSchema.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore().setValues({open: true, employees: 3});
const schema = createTools(store).getStoreValuesSchema();

console.log(schema);
// -> {open: {type: 'boolean'}, employees: {type: 'number'}}
Since

v3.0.0

Statistics methods

This is the collection of statistics methods within the Tools interface. There is only one method, getStoreStats.

getStoreStats

The getStoreStats method provides a set of statistics about the Store, and is used for debugging purposes.

getStoreStats(detail?: boolean): StoreStats
TypeDescription
detail?boolean

An optional boolean that indicates more detailed stats about the inner structure of the Store should be returned.

returnsStoreStats

A StoreStats object containing statistics about the Store.

Examples

This example creates a Tools object and gets basic statistics about it.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
  })
  .setValues({open: true, employees: 3});
console.log(createTools(store).getStoreStats());
// -> {totalTables: 2, totalRows: 5, totalCells: 8, totalValues: 2, jsonLength: 212}

This example creates a Tools object and gets detailed statistics about it.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
  });
const stats = createTools(store).getStoreStats(true);

console.log(stats.totalTables);
// -> 2
console.log(stats.totalRows);
// -> 5
console.log(stats.totalCells);
// -> 8
console.log(stats.detail.tables.pets.tableRows);
// -> 3
console.log(stats.detail.tables.pets.tableCells);
// -> 6
console.log(stats.detail.tables.pets.rows);
// -> {fido: {rowCells: 2}, felix: {rowCells: 2}, cujo: {rowCells: 2}}
Since

v2.2.0

Functions

There is one function, createTools, within the tools module.

createTools

The createTools function creates a Tools object, and is the main entry point into the tools module.

createTools(store: Store): Tools
TypeDescription
storeStore

The Store for which to register tools.

returnsTools

A reference to the new Tools object.

A given Store can only have one Tools object associated with it. If you call this function twice on the same Store, your second call will return a reference to the Tools object created by the first.

Examples

This example creates a Tools object.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore()
  .setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  })
  .setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
  })
  .setValues({open: true, employees: 3});
console.log(createTools(store).getStoreStats());
// -> {totalTables: 2, totalRows: 5, totalCells: 8, totalValues: 2, jsonLength: 212}

This example creates a Tools object, and calls the method a second time for the same Store to return the same object.

import {createStore} from 'tinybase';
import {createTools} from 'tinybase/tools';

const store = createStore();
const tools1 = createTools(store);
const tools2 = createTools(store);
console.log(tools1 === tools2);
// -> true
Since

v2.2.0

Type Aliases

These are the type aliases within the tools module.

StoreStats

The StoreStats type describes a set of statistics about the Store, and is used for debugging purposes.

{
  totalTables: number;
  totalRows: number;
  totalCells: number;
  totalValues: number;
  jsonLength: number;
  detail?: StoreStatsDetail;
}
TypeDescription
totalTablesnumber

The number of Table objects in the Store.

totalRowsnumber

The number of Row objects in the Store, across all Table objects.

totalCellsnumber

The number of Cell objects in the Store, across all Row objects, across all Table objects.

totalValuesnumber

The number of Value objects in the Store, since v3.0.

jsonLengthnumber

The string length of the Store when serialized to JSON.

detail?StoreStatsDetail

Additional detailed statistics about the Store if the detail flag is specified in the getStoreStats method.

A StoreStats object is returned from the getStoreStats method.

Since

v2.2.0

StoreStatsDetail

The StoreStatsDetail type describes a more detailed set of statistics about the Store, and is used for debugging purposes.

{tables: {[tableId: Id]: StoreStatsTableDetail}}
TypeDescription
tables{[tableId: Id]: StoreStatsTableDetail}

Information about each Table in the Store.

A StoreStatsDetail object is added to the StoreStats object (returned from the getStoreStats method) when the detail flag is specified.

Since

v2.2.0

StoreStatsRowDetail

The StoreStatsRowDetail type describes statistics about a single Row in the Store, and is used for debugging purposes.

{rowCells: number}
TypeDescription
rowCellsnumber

The number of Cell objects in the Row.

Since

v2.2.0

StoreStatsTableDetail

The StoreStatsTableDetail type describes a detailed set of statistics about a single Table in the Store, and is used for debugging purposes.

{
  tableRows: number;
  tableCells: number;
  rows: {[rowId: Id]: StoreStatsRowDetail};
}
TypeDescription
tableRowsnumber

The number of Row objects in the Table.

tableCellsnumber

The number of Cell objects in the Table, across all Row objects.

rows{[rowId: Id]: StoreStatsRowDetail}

Detail about the Table object.

Since

v2.2.0

ui-react

The ui-react module of the TinyBase project provides both hooks and components to make it easy to create reactive apps with Store objects.

The hooks in this module provide access to the data and structures exposed by other modules in the project. As well as immediate access, they all register listeners such that components using those hooks are selectively re-rendered when data changes.

The components in this module provide a further abstraction over those hooks to ease the composition of user interfaces that use TinyBase.

See also

Since

v1.0.0

Functions

These are the functions within the ui-react module.

Checkpoints hooks

This is the collection of checkpoints hooks within the ui-react module. There are 14 checkpoints hooks in total.

useCheckpoint

The useCheckpoint hook returns the label for a checkpoint, and registers a listener so that any changes to that result will cause a re-render.

useCheckpoint(
  checkpointId: string,
  checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId,
): string | undefined
TypeDescription
checkpointIdstring

The Id of the checkpoint.

checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to be accessed: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

returnsstring | undefined

A string label for the requested checkpoint, an empty string if it was never set, or undefined if the checkpoint does not exist.

A Provider component is used to wrap part of an application in a context, and it can contain a default Checkpoints object or a set of Checkpoints objects named by Id. The useCheckpoint hook lets you indicate which Checkpoints object to get data for: omit the optional final parameter for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide a Checkpoints object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the label will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Checkpoints object outside the application, which is used in the useCheckpoint hook by reference. A change to the checkpoint label re-renders the component.

import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useCheckpoint} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {fido: {species: 'dog'}});
const checkpoints = createCheckpoints(store);
const App = () => <span>{useCheckpoint('1', checkpoints)}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span></span>'

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(app.innerHTML);
// -> '<span>sale</span>'

This example creates a Provider context into which a default Checkpoints object is provided. A component within it then uses the useCheckpoint hook.

import {Provider, useCheckpoint} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useCheckpoint('0')}</span>;

const checkpoints = createCheckpoints(
  createStore().setTable('pets', {fido: {species: 'dog'}}),
).setCheckpoint('0', 'initial');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<span>initial</span>'

This example creates a Provider context into which a default Checkpoints object is provided. A component within it then uses the useCheckpoint hook.

import {Provider, useCheckpoint} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpointsById={{petCheckpoints: checkpoints}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useCheckpoint('0', 'petCheckpoints')}</span>;

const checkpoints = createCheckpoints(
  createStore().setTable('pets', {fido: {species: 'dog'}}),
).setCheckpoint('0', 'initial');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<span>initial</span>'
Since

v1.0.0

useCheckpointIds

The useCheckpointIds hook returns an array of the checkpoint Ids being managed by this Checkpoints object, and registers a listener so that any changes to that result will cause a re-render.

useCheckpointIds(checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId): CheckpointIds
TypeDescription
checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to be accessed: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

returnsCheckpointIds

A CheckpointIds array, containing the checkpoint Ids managed by this Checkpoints object.

A Provider component is used to wrap part of an application in a context, and it can contain a default Checkpoints object or a set of Checkpoints objects named by Id. The useCheckpointIds hook lets you indicate which Checkpoints object to get data for: omit the optional parameter for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide a Checkpoints object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the checkpoint Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Checkpoints object outside the application, which is used in the useCheckpointIds hook by reference. A change to the checkpoint Ids re-renders the component.

import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useCheckpointIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {fido: {species: 'dog'}});
const checkpoints = createCheckpoints(store);
const App = () => (
  <span>{JSON.stringify(useCheckpointIds(checkpoints))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>[[],"0",[]]</span>'

store.setCell('pets', 'fido', 'sold', true);
console.log(app.innerHTML);
// -> '<span>[["0"],null,[]]</span>'

checkpoints.addCheckpoint('sale');
console.log(app.innerHTML);
// -> '<span>[["0"],"1",[]]</span>'

This example creates a Provider context into which a default Checkpoints object is provided. A component within it then uses the useCheckpointIds hook.

import {Provider, useCheckpointIds} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useCheckpointIds())}</span>;

const checkpoints = createCheckpoints(
  createStore().setTable('pets', {fido: {species: 'dog'}}),
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<span>[[],"0",[]]</span>'

This example creates a Provider context into which a default Checkpoints object is provided. A component within it then uses the useCheckpointIds hook.

import {Provider, useCheckpointIds} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpointsById={{petCheckpoints: checkpoints}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useCheckpointIds('petCheckpoints'))}</span>
);

const checkpoints = createCheckpoints(
  createStore().setTable('pets', {fido: {species: 'dog'}}),
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<span>[[],"0",[]]</span>'
Since

v1.0.0

useCheckpointIdsListener

The useCheckpointIdsListener hook registers a listener function with the Checkpoints object that will be called whenever its set of checkpoints changes.

useCheckpointIdsListener(
  listener: CheckpointIdsListener,
  listenerDeps?: DependencyList,
  checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId,
): void
TypeDescription
listenerCheckpointIdsListener

The function that will be called whenever the checkpoints change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to register the listener with: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useCheckpointIds hook).

Unlike the addCheckpointIdsListener method, which returns a listener Id and requires you to remove it manually, the useCheckpointIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Checkpoints object will be deleted.

Example

This example uses the useCheckpointIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useCheckpointIdsListener} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useCheckpointIdsListener(() => console.log('Checkpoint Ids changed'));
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {sold: false}}});
const checkpoints = createCheckpoints(store);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(checkpoints.getListenerStats().checkpointIds);
// -> 1

store.setCell('pets', 'fido', 'sold', true);
// -> 'Checkpoint Ids changed'
checkpoints.addCheckpoint();
// -> 'Checkpoint Ids changed'

root.unmount();
console.log(checkpoints.getListenerStats().checkpointIds);
// -> 0
Since

v1.0.0

useCheckpointListener

The useCheckpointListener hook registers a listener function with the Checkpoints object that will be called whenever the label of a checkpoint changes.

useCheckpointListener(
  checkpointId: IdOrNull,
  listener: CheckpointListener,
  listenerDeps?: DependencyList,
  checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId,
): void
TypeDescription
checkpointIdIdOrNull

The Id of the checkpoint to listen to, or null as a wildcard.

listenerCheckpointListener

The function that will be called whenever the checkpoint label changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to register the listener with: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useCheckpoint hook).

You can either listen to a single checkpoint label (by specifying the checkpoint Id as the method's first parameter), or changes to any checkpoint label (by providing a null wildcard).

Unlike the addCheckpointListener method, which returns a listener Id and requires you to remove it manually, the useCheckpointListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Checkpoints object will be deleted.

Example

This example uses the useCheckpointListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useCheckpointListener} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useCheckpointListener('0', () =>
    console.log('Checkpoint label changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {sold: false}}});
const checkpoints = createCheckpoints(store);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(checkpoints.getListenerStats().checkpoint);
// -> 1

checkpoints.setCheckpoint('0', 'initial');
// -> 'Checkpoint label changed'

root.unmount();
console.log(checkpoints.getListenerStats().checkpoint);
// -> 0
Since

v1.0.0

useCheckpoints

The useCheckpoints hook is used to get a reference to a Checkpoints object from within a Provider component context.

useCheckpoints(id?: string): Checkpoints | undefined
TypeDescription
id?string

An optional Id for accessing a Checkpoints object that was named with an Id in the Provider.

returnsCheckpoints | undefined

A reference to the Checkpoints object (or undefined if not within a Provider context, or if the requested Checkpoints object does not exist).

A Provider component is used to wrap part of an application in a context. It can contain a default Checkpoints object (or a set of Checkpoints objects named by Id) that can be easily accessed without having to be passed down as props through every component.

The useCheckpoints hook lets you either get a reference to the default Checkpoints object (when called without a parameter), or one of the Checkpoints objects that are named by Id (when called with an Id parameter).

Examples

This example creates a Provider context into which a default Checkpoint object is provided. A component within it then uses the useCheckpoints hook to get a reference to the Checkpoints object again, without the need to have it passed as a prop.

import {Provider, useCheckpoints} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useCheckpoints().getListenerStats().checkpointIds}</span>
);

const checkpoints = createCheckpoints(createStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Checkpoints object is provided, named by Id. A component within it then uses the useCheckpoints hook with that Id to get a reference to the Checkpoints object again, without the need to have it passed as a prop.

import {Provider, useCheckpoints} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpointsById={{petStore: checkpoints}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {useCheckpoints('petStore').getListenerStats().checkpointIds}
  </span>
);

const checkpoints = createCheckpoints(createStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v1.0.0

useCheckpointsIds

The useCheckpointsIds hook is used to retrieve the Ids of all the named Checkpoints objects present in the current Provider component context.

useCheckpointsIds(): Ids
returnsIds

A list of the Ids in the context.

Example

This example adds two named Checkpoints objects to a Provider context and an inner component accesses their Ids.

import {
  Provider,
  useCheckpointsIds,
  useCreateCheckpoints,
  useCreateStore,
} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store1 = useCreateStore(createStore);
  const checkpoints1 = useCreateCheckpoints(store1, createCheckpoints);
  const store2 = useCreateStore(createStore);
  const checkpoints2 = useCreateCheckpoints(store2, createCheckpoints);
  return (
    <Provider checkpointsById={{checkpoints1, checkpoints2}}>
      <Pane />
    </Provider>
  );
};
const Pane = () => <span>{JSON.stringify(useCheckpointsIds())}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["checkpoints1","checkpoints2"]</span>'
Since

v4.1.0

useCheckpointsOrCheckpointsById

The useCheckpointsOrCheckpointsById hook is used to get a reference to a Checkpoints object from within a Provider component context, or have it passed directly to this hook.

useCheckpointsOrCheckpointsById(checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId): Checkpoints | undefined
TypeDescription
checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

Either an Id for accessing a Checkpoints object that was named with an Id in the Provider, or the Checkpoints object itself.

returnsCheckpoints | undefined

A reference to the Checkpoints object (or undefined if not within a Provider context, or if the requested Checkpoints object does not exist).

This is mostly of use when you are developing a component that needs a Checkpoints object and which might have been passed in explicitly to the component or is to be picked up from the context by Id (a common pattern for Checkpoints-based components).

This is unlikely to be used often. For most situations, you will want to use the useCheckpoints hook.

Example

This example creates a Provider context into which a default Checkpoints object is provided. A component within it then uses the useCheckpointsOrCheckpointsById hook to get a reference to the Checkpoints object again, without the need to have it passed as a prop. Note however, that unlike the useCheckpoints hook example, this component would also work if you were to pass the Checkpoints object directly into it, making it more portable.

import {
  Provider,
  useCheckpointsOrCheckpointsById,
} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = ({checkpoints}) => (
  <span>
    {JSON.stringify(
      useCheckpointsOrCheckpointsById(checkpoints).getCheckpointIds(),
    )}
  </span>
);

const checkpoints = createCheckpoints(createStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<span>[[],"0",[]]</span>'
Since

v4.1.0

useCreateCheckpoints

The useCreateCheckpoints hook is used to create a Checkpoints object within a React application with convenient memoization.

useCreateCheckpoints(
  store: undefined | Store,
  create: (store: Store) => Checkpoints,
  createDeps?: DependencyList,
): Checkpoints | undefined
TypeDescription
storeundefined | Store

A reference to the Store for which to create a new Checkpoints object.

create(store: Store) => Checkpoints

A function for performing the creation steps of the Checkpoints object for the Store, plus any additional steps such as adding definitions or listeners, and returning it.

createDeps?DependencyList

An optional array of dependencies for the create function, which, if any change, result in its rerun. This parameter defaults to an empty array.

returnsCheckpoints | undefined

A reference to the Checkpoints object.

It is possible to create a Checkpoints object outside of the React app with the regular createCheckpoints function and pass it in, but you may prefer to create it within the app, perhaps inside the top-level component. To prevent a new Checkpoints object being created every time the app renders or re-renders, since v5.0 this hook performs the creation in an effect. As a result it will return undefined on the brief first render (or if the Store is not yet defined), which you should defend against.

If your create function contains other dependencies, the changing of which should also cause the Checkpoints object to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

This hook ensures the Checkpoints object is destroyed whenever a new one is created or the component is unmounted.

Examples

This example creates a Checkpoints object at the top level of a React application. Even though the App component is rendered twice, the Checkpoints object creation only occurs once by default.

import {createCheckpoints, createStore} from 'tinybase';
import {useCreateCheckpoints, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store = useCreateStore(createStore);
  const checkpoints = useCreateCheckpoints(store, (store) => {
    console.log('Checkpoints created');
    return createCheckpoints(store).setSize(10);
  });
  return <span>{JSON.stringify(checkpoints?.getCheckpointIds())}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Checkpoints created'

root.render(<App />);
// No second Checkpoints creation

console.log(app.innerHTML);
// -> '<span>[[],"0",[]]</span>'

This example creates a Checkpoints object at the top level of a React application. The App component is rendered twice, each with a different top-level prop. The useCreateCheckpoints hook takes the size prop as a dependency, and so the Checkpoints object is created again on the second render.

import {createCheckpoints, createStore} from 'tinybase';
import {useCreateCheckpoints, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({size}) => {
  const store = useCreateStore(createStore);
  const checkpoints = useCreateCheckpoints(
    store,
    (store) => {
      console.log(`Checkpoints created, size ${size}`);
      return createCheckpoints(store).setSize(size);
    },
    [size],
  );
  return <span>{JSON.stringify(checkpoints?.getCheckpointIds())}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App size={20} />);
// -> 'Checkpoints created, size 20'

console.log(app.innerHTML);
// -> '<span>[[],"0",[]]</span>'

root.render(<App size={50} />);
// -> 'Checkpoints created, size 50'

console.log(app.innerHTML);
// -> '<span>[[],"0",[]]</span>'
Since

v1.0.0

useGoBackwardCallback

The useGoBackwardCallback hook returns a callback that moves the state of the underlying Store back to the previous checkpoint, effectively performing an 'undo' on the Store data.

useGoBackwardCallback(checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId): Callback
TypeDescription
checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to use to go backward: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

returnsCallback

A callback for subsequent use.

This hook is useful, for example, when creating an event handler that will go backward to the previous checkpoint - such as when clicking an undo button.

If there is no previous checkpoint to return to, this callback has no effect.

Example

This example uses the useGoBackwardCallback hook to create an event handler which goes backward in the checkpoint stack when the span element is clicked.

import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useGoBackwardCallback} from 'tinybase/ui-react';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const checkpoints = createCheckpoints(store);
const App = () => (
  <span id="span" onClick={useGoBackwardCallback(checkpoints)}>
    Backward
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
const _span = app.querySelector('span');

store.setCell('pets', 'nemo', 'color', 'orange');
checkpoints.addCheckpoint();
console.log(checkpoints.getCheckpointIds());
// -> [["0"], "1", []]

// User clicks the <span> element:
// -> _span MouseEvent('click', {bubbles: true})
console.log(checkpoints.getCheckpointIds());
// -> [[], "0", ["1"]]
Since

v1.0.0

useGoForwardCallback

The useGoForwardCallback hook returns a callback that moves the state of the underlying Store forwards to a future checkpoint, effectively performing an 'redo' on the Store data.

useGoForwardCallback(checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId): Callback
TypeDescription
checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to use to go backward: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

returnsCallback

A callback for subsequent use.

This hook is useful, for example, when creating an event handler that will go forward to the next checkpoint - such as when clicking an redo button.

If there is no future checkpoint to return to, this callback has no effect.

Example

This example uses the useGoForwardCallback hook to create an event handler which goes backward in the checkpoint stack when the span element is clicked.

import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useGoForwardCallback} from 'tinybase/ui-react';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const checkpoints = createCheckpoints(store);
const App = () => (
  <span id="span" onClick={useGoForwardCallback(checkpoints)}>
    Forward
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
const _span = app.querySelector('span');

store.setCell('pets', 'nemo', 'color', 'orange');
checkpoints.addCheckpoint();
console.log(checkpoints.getCheckpointIds());
// -> [["0"], "1", []]

checkpoints.goBackward();
console.log(checkpoints.getCheckpointIds());
// -> [[], "0", ["1"]]

// User clicks the <span> element:
// -> _span MouseEvent('click', {bubbles: true})
console.log(checkpoints.getCheckpointIds());
// -> [["0"], "1", []]
Since

v1.0.0

useGoToCallback

The useGoToCallback hook returns a parameterized callback that can be used to move the state of the underlying Store backwards or forwards to a specified checkpoint.

useGoToCallback<Parameter>(
  getCheckpointId: (parameter: Parameter) => string,
  getCheckpointIdDeps?: DependencyList,
  checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId,
  then?: (checkpoints: Checkpoints, checkpointId: string) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
getCheckpointId(parameter: Parameter) => string

A function which returns an Id that will be used to indicate which checkpoint to move to, based on the parameter the callback will receive (and which is most likely a DOM event).

getCheckpointIdDeps?DependencyList

An optional array of dependencies for the getCheckpointId function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to be updated: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

then?(checkpoints: Checkpoints, checkpointId: string) => void

A function which is called after the checkpoint is moved, with a reference to the Checkpoints object and the checkpoint Id moved to.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use. This parameter defaults to an empty array.

This hook is useful, for example, when creating an event handler that will move the checkpoint. In this case, the parameter will likely be the event, so that you can use data from it as the checkpoint Id to move to.

The optional first parameter is a function which will produce the label that will then be used to name the checkpoint.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the checkpoint has been set.

The Checkpoints object for which the callback will set the checkpoint (indicated by the hook's checkpointsOrCheckpointsId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useGoToCallback hook to create an event handler which moves to a checkpoint when the span element is clicked.

import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useGoToCallback} from 'tinybase/ui-react';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const checkpoints = createCheckpoints(store);
const App = () => {
  const handleClick = useGoToCallback(() => '0', [], checkpoints);
  return (
    <span id="span" onClick={handleClick}>
      Goto 0
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const _span = app.querySelector('span');

store.setCell('pets', 'nemo', 'color', 'orange');
checkpoints.addCheckpoint();
console.log(checkpoints.getCheckpointIds());
// -> [["0"], "1", []]

// User clicks the <span> element:
// -> _span MouseEvent('click', {bubbles: true})
console.log(checkpoints.getCheckpointIds());
// -> [[], "0", ["1"]]
Since

v1.0.0

useRedoInformation

The useRedoInformation hook returns an UndoOrRedoInformation array that indicates if and how you can move the state of the underlying Store forwards to a future checkpoint.

useRedoInformation(checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId): UndoOrRedoInformation
TypeDescription
checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to use to go backward: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

returnsUndoOrRedoInformation

UndoOrRedoInformation about if and how you can move the state of the underlying Store forward.

This hook is useful if you are building an redo button: the information contains whether a redo action is available (to enable the button), the callback to perform the redo action, the checkpoint Id that will be redone, and its label, if available.

Example

This example uses the useUndoInformation hook to create a redo button.

import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useRedoInformation} from 'tinybase/ui-react';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const checkpoints = createCheckpoints(store);
const App = () => {
  const [canRedo, handleRedo, _id, label] =
    useRedoInformation(checkpoints);
  return canRedo ? (
    <span onClick={handleRedo}>Redo {label}</span>
  ) : (
    <span>Nothing to redo</span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>Nothing to redo</span>'

store.setCell('pets', 'nemo', 'color', 'orange');
checkpoints.addCheckpoint('color');
checkpoints.goTo('0');
console.log(app.innerHTML);
// -> '<span>Redo color</span>'
Since

v1.0.0

useSetCheckpointCallback

The useSetCheckpointCallback hook returns a parameterized callback that can be used to record a checkpoint of a Store into a Checkpoints object that can be reverted to in the future.

useSetCheckpointCallback<Parameter>(
  getCheckpoint?: (parameter: Parameter) => string,
  getCheckpointDeps?: DependencyList,
  checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId,
  then?: (checkpointId: string, checkpoints: Checkpoints, label?: string) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
getCheckpoint?(parameter: Parameter) => string

An optional function which returns a string that will be used to describe the actions leading up to this checkpoint, based on the parameter the callback will receive (and which is most likely a DOM event).

getCheckpointDeps?DependencyList

An optional array of dependencies for the getCheckpoint function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to be updated: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

then?(checkpointId: string, checkpoints: Checkpoints, label?: string) => void

A function which is called after the checkpoint is set, with the new checkpoint Id, a reference to the Checkpoints object and the label provided, if any.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will set the checkpoint. In this case, the parameter will likely be the event, so that you can use data from it as the checkpoint label.

The optional first parameter is a function which will produce the label that will then be used to name the checkpoint.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the checkpoint has been set.

The Checkpoints object for which the callback will set the checkpoint (indicated by the hook's checkpointsOrCheckpointsId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useSetCheckpointCallback hook to create an event handler which sets a checkpoint when the span element is clicked.

import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useSetCheckpointCallback} from 'tinybase/ui-react';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const checkpoints = createCheckpoints(store);
const App = () => {
  const handleClick = useSetCheckpointCallback(
    (e) => `with #${e.target.id} button`,
    [],
    checkpoints,
    (checkpointId, checkpoints, label) =>
      console.log(`Checkpoint ${checkpointId} set, ${label}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      Set
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const _span = app.querySelector('span');

store.setCell('pets', 'nemo', 'color', 'orange');

// User clicks the <span> element:
// -> _span MouseEvent('click', {bubbles: true})
// -> 'Checkpoint 1 set, with #span button'
Since

v1.0.0

useUndoInformation

The useUndoInformation hook returns an UndoOrRedoInformation array that indicates if and how you can move the state of the underlying Store backward to the previous checkpoint.

useUndoInformation(checkpointsOrCheckpointsId?: CheckpointsOrCheckpointsId): UndoOrRedoInformation
TypeDescription
checkpointsOrCheckpointsId?CheckpointsOrCheckpointsId

The Checkpoints object to use to go backward: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

returnsUndoOrRedoInformation

UndoOrRedoInformation about if and how you can move the state of the underlying Store backward.

This hook is useful if you are building an undo button: the information contains whether an undo action is available (to enable the button), the callback to perform the undo action, the current checkpoint Id that will be undone, and its label, if available.

Example

This example uses the useUndoInformation hook to create an undo button.

import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useUndoInformation} from 'tinybase/ui-react';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const checkpoints = createCheckpoints(store);
const App = () => {
  const [canUndo, handleUndo, _id, label] =
    useUndoInformation(checkpoints);
  return canUndo ? (
    <span onClick={handleUndo}>Undo {label}</span>
  ) : (
    <span>Nothing to undo</span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>Nothing to undo</span>'

store.setCell('pets', 'nemo', 'color', 'orange');
checkpoints.addCheckpoint('color');
console.log(app.innerHTML);
// -> '<span>Undo color</span>'
Since

v1.0.0

Indexes hooks

This is the collection of indexes hooks within the ui-react module. There are 9 indexes hooks in total.

useSliceRowIds

The useSliceRowIds hook gets the list of Row Ids in a given Slice, and registers a listener so that any changes to that result will cause a re-render.

useSliceRowIds(
  indexId: string,
  sliceId: string,
  indexesOrIndexesId?: IndexesOrIndexesId,
): Ids
TypeDescription
indexIdstring

The Id of the Index.

sliceIdstring

The Id of the Slice in the Index.

indexesOrIndexesId?IndexesOrIndexesId

The Indexes object to be accessed: omit for the default context Indexes object, provide an Id for a named context Indexes object, or provide an explicit reference.

returnsIds

The Row Ids in the Slice, or an empty array.

A Provider component is used to wrap part of an application in a context, and it can contain a default Indexes object or a set of Indexes objects named by Id. The useSliceRowIds hook lets you indicate which Indexes object to get data for: omit the optional final parameter for the default context Indexes object, provide an Id for a named context Indexes object, or provide an Indexes object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Row Ids in the Slice will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates an Indexes object outside the application, which is used in the useSliceRowIds hook by reference. A change to the Row Ids in the Slice re-renders the component.

import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useSliceRowIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
const App = () => (
  <span>
    {JSON.stringify(useSliceRowIds('bySpecies', 'dog', indexes))}
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'

store.setRow('pets', 'toto', {species: 'dog'});
console.log(app.innerHTML);
// -> '<span>["fido","cujo","toto"]</span>'

This example creates a Provider context into which a default Indexes object is provided. A component within it then uses the useSliceRowIds hook.

import {Provider, useSliceRowIds} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useSliceRowIds('bySpecies', 'dog'))}</span>
);

const indexes = createIndexes(
  createStore().setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  }),
).setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'

This example creates a Provider context into which a default Indexes object is provided. A component within it then uses the useSliceRowIds hook.

import {Provider, useSliceRowIds} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexesById={{petIndexes: indexes}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(useSliceRowIds('bySpecies', 'dog', 'petIndexes'))}
  </span>
);

const indexes = createIndexes(
  createStore().setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  }),
).setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'
Since

v1.0.0

useSliceRowIdsListener

The useSliceRowIdsListener hook registers a listener function with the Indexes object that will be called whenever the Row Ids in a Slice change.

useSliceRowIdsListener(
  indexId: IdOrNull,
  sliceId: IdOrNull,
  listener: SliceRowIdsListener,
  listenerDeps?: DependencyList,
  indexesOrIndexesId?: IndexesOrIndexesId,
): void
TypeDescription
indexIdIdOrNull

The Id of the Index to listen to, or null as a wildcard.

sliceIdIdOrNull

The Id of the Slice to listen to, or null as a wildcard.

listenerSliceRowIdsListener

The function that will be called whenever the Row Ids in the Slice change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

indexesOrIndexesId?IndexesOrIndexesId

The Indexes object to register the listener with: omit for the default context Indexes object, provide an Id for a named context Indexes object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useSliceRowIds hook).

You can either listen to a single Slice (by specifying the Index Id and Slice Id as the method's first two parameters), or changes to any Slice (by providing null wildcards).

Both, either, or neither of the indexId and sliceId parameters can be wildcarded with null. You can listen to a specific Slice in a specific Index, any Slice in a specific Index, a specific Slice in any Index, or any Slice in any Index.

Unlike the addSliceRowIdsListener method, which returns a listener Id and requires you to remove it manually, the useSliceRowIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Indexes object will be deleted.

Example

This example uses the useSliceRowIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Indexes object.

import {Provider, useSliceRowIdsListener} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useSliceRowIdsListener('bySpecies', 'dog', () =>
    console.log('Slice Row Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App indexes={indexes} />);
console.log(indexes.getListenerStats().sliceRowIds);
// -> 1

store.setRow('pets', 'toto', {species: 'dog'});
// -> 'Slice Row Ids changed'

root.unmount();
console.log(indexes.getListenerStats().sliceRowIds);
// -> 0
Since

v1.0.0

useCreateIndexes

The useCreateIndexes hook is used to create an Indexes object within a React application with convenient memoization.

useCreateIndexes(
  store: undefined | Store,
  create: (store: Store) => Indexes,
  createDeps?: DependencyList,
): Indexes | undefined
TypeDescription
storeundefined | Store

A reference to the Store for which to create a new Indexes object.

create(store: Store) => Indexes

A function for performing the creation steps of the Indexes object for the Store, plus any additional steps such as adding definitions or listeners, and returning it.

createDeps?DependencyList

An optional array of dependencies for the create function, which, if any change, result in its rerun. This parameter defaults to an empty array.

returnsIndexes | undefined

A reference to the Indexes object.

It is possible to create an Indexes object outside of the React app with the regular createIndexes function and pass it in, but you may prefer to create it within the app, perhaps inside the top-level component. To prevent a new Indexes object being created every time the app renders or re-renders, since v5.0 the this hook performs the creation in an effect. As a result it will return undefined on the brief first render (or if the Store is not yet defined), which you should defend against.

If your create function contains other dependencies, the changing of which should also cause the Indexes object to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

This hook ensures the Indexes object is destroyed whenever a new one is created or the component is unmounted.

Examples

This example creates an Indexes object at the top level of a React application. Even though the App component is rendered twice, the Indexes object creation only occurs once by default.

import {createIndexes, createStore} from 'tinybase';
import {useCreateIndexes, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store = useCreateStore(() =>
    createStore().setTable('pets', {
      fido: {species: 'dog'},
      felix: {species: 'cat'},
      cujo: {species: 'dog'},
    }),
  );
  const indexes = useCreateIndexes(store, (store) => {
    console.log('Indexes created');
    return createIndexes(store).setIndexDefinition(
      'bySpecies',
      'pets',
      'species',
    );
  });
  return <span>{JSON.stringify(indexes?.getSliceIds('bySpecies'))}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Indexes created'

root.render(<App />);
// No second Indexes creation

console.log(app.innerHTML);
// -> '<span>["dog","cat"]</span>'

This example creates an Indexes object at the top level of a React application. The App component is rendered twice, each with a different top-level prop. The useCreateIndexes hook takes the cellToIndex prop as a dependency, and so the Indexes object is created again on the second render.

import {createIndexes, createStore} from 'tinybase';
import {useCreateIndexes, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({cellToIndex}) => {
  const store = useCreateStore(() =>
    createStore().setTable('pets', {
      fido: {species: 'dog', color: 'brown'},
      felix: {species: 'cat', color: 'black'},
      cujo: {species: 'dog', color: 'brown'},
    }),
  );
  const indexes = useCreateIndexes(
    store,
    (store) => {
      console.log(`Index created for ${cellToIndex} cell`);
      return createIndexes(store).setIndexDefinition(
        'byCell',
        'pets',
        cellToIndex,
      );
    },
    [cellToIndex],
  );
  return <span>{JSON.stringify(indexes?.getSliceIds('byCell'))}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App cellToIndex="species" />);
// -> 'Index created for species cell'

console.log(app.innerHTML);
// -> '<span>["dog","cat"]</span>'

root.render(<App cellToIndex="color" />);
// -> 'Index created for color cell'

console.log(app.innerHTML);
// -> '<span>["brown","black"]</span>'
Since

v1.0.0

useIndexIds

The useIndexIds hook gets an array of the Index Ids registered with an Indexes object, and registers a listener so that any changes to that result will cause a re-render.

useIndexIds(indexesOrIndexesId?: IndexesOrIndexesId): Ids
TypeDescription
indexesOrIndexesId?IndexesOrIndexesId

The Indexes object to be accessed: omit for the default context Indexes object, provide an Id for a named context Indexes object, or provide an explicit reference.

returnsIds

The Index Ids in the Indexes object, or an empty array.

A Provider component is used to wrap part of an application in a context, and it can contain a default Indexes object or a set of Indexes objects named by Id. The useIndexIds hook lets you indicate which Indexes object to get data for: omit the optional final parameter for the default context Indexes object, provide an Id for a named context Indexes object, or provide an Indexes object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Index Ids in the Indexes object will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Example

This example creates an Indexes object outside the application, which is used in the useIndexIds hook by reference. A newly-registered Index re-renders the component.

import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useIndexIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
const App = () => <span>{JSON.stringify(useIndexIds(indexes))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>[]</span>'

indexes.setIndexDefinition('bySpecies', 'pets', 'species');
console.log(app.innerHTML);
// -> '<span>["bySpecies"]</span>'
Since

v4.1.0

useIndexes

The useIndexes hook is used to get a reference to an Indexes object from within a Provider component context.

useIndexes(id?: string): Indexes | undefined
TypeDescription
id?string

An optional Id for accessing an Indexes object that was named with an Id in the Provider.

returnsIndexes | undefined

A reference to the Indexes object (or undefined if not within a Provider context, or if the requested Indexes object does not exist).

A Provider component is used to wrap part of an application in a context. It can contain a default Indexes object (or a set of Indexes objects named by Id) that can be easily accessed without having to be passed down as props through every component.

The useIndexes hook lets you either get a reference to the default Indexes object (when called without a parameter), or one of the Indexes objects that are named by Id (when called with an Id parameter).

Examples

This example creates a Provider context into which a default Indexes object is provided. A component within it then uses the useIndexes hook to get a reference to the Indexes object again, without the need to have it passed as a prop.

import {Provider, useIndexes} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useIndexes().getListenerStats().sliceIds}</span>;

const indexes = createIndexes(createStore());
const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which an Indexes object is provided, named by Id. A component within it then uses the useIndexes hook with that Id to get a reference to the Indexes object again, without the need to have it passed as a prop.

import {Provider, useIndexes} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexesById={{petStore: indexes}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useIndexes('petStore').getListenerStats().sliceIds}</span>
);

const indexes = createIndexes(createStore());
const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v1.0.0

useIndexesIds

The useIndexesIds hook is used to retrieve the Ids of all the named Indexes objects present in the current Provider component context.

useIndexesIds(): Ids
returnsIds

A list of the Ids in the context.

Example

This example adds two named Indexes objects to a Provider context and an inner component accesses their Ids.

import {
  Provider,
  useCreateIndexes,
  useCreateStore,
  useIndexesIds,
} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store1 = useCreateStore(createStore);
  const indexes1 = useCreateIndexes(store1, createIndexes);
  const store2 = useCreateStore(createStore);
  const indexes2 = useCreateIndexes(store2, createIndexes);
  return (
    <Provider indexesById={{indexes1, indexes2}}>
      <Pane />
    </Provider>
  );
};
const Pane = () => <span>{JSON.stringify(useIndexesIds())}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["indexes1","indexes2"]</span>'
Since

v4.1.0

useIndexesOrIndexesById

The useIndexesOrIndexesById hook is used to get a reference to an Indexes object from within a Provider component context, or have it passed directly to this hook.

useIndexesOrIndexesById(indexesOrIndexesId?: IndexesOrIndexesId): Indexes | undefined
TypeDescription
indexesOrIndexesId?IndexesOrIndexesId

Either an Id for accessing a Indexes object that was named with an Id in the Provider, or the Indexes object itself.

returnsIndexes | undefined

A reference to the Indexes object (or undefined if not within a Provider context, or if the requested Indexes object does not exist).

This is mostly of use when you are developing a component that needs an Indexes object and which might have been passed in explicitly to the component or is to be picked up from the context by Id (a common pattern for Indexes-based components).

This hook is unlikely to be used often. For most situations, you will want to use the useIndexes hook.

Example

This example creates a Provider context into which a default Indexes object is provided. A component within it then uses the useIndexesOrIndexesById hook to get a reference to the Indexes object again, without the need to have it passed as a prop. Note however, that unlike the useIndexes hook example, this component would also work if you were to pass the Indexes object directly into it, making it more portable.

import {Provider, useIndexesOrIndexesById} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = ({indexes}) => (
  <span>
    {JSON.stringify(useIndexesOrIndexesById(indexes).getIndexIds())}
  </span>
);

const indexes = createIndexes(
  createStore().setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  }),
).setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<span>["bySpecies"]</span>'
Since

v4.1.0

useSliceIds

The useSliceIds hook gets the list of Slice Ids in an Index, and registers a listener so that any changes to that result will cause a re-render.

useSliceIds(
  indexId: string,
  indexesOrIndexesId?: IndexesOrIndexesId,
): Ids
TypeDescription
indexIdstring

The Id of the Index.

indexesOrIndexesId?IndexesOrIndexesId

The Indexes object to be accessed: omit for the default context Indexes object, provide an Id for a named context Indexes object, or provide an explicit reference.

returnsIds

The Slice Ids in the Index, or an empty array.

A Provider component is used to wrap part of an application in a context, and it can contain a default Indexes object or a set of Indexes objects named by Id. The useSliceIds hook lets you indicate which Indexes object to get data for: omit the optional final parameter for the default context Indexes object, provide an Id for a named context Indexes object, or provide a Indexes object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Slice Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates an Indexes object outside the application, which is used in the useSliceIds hook by reference. A change to the Slice Ids re-renders the component.

import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useSliceIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
const App = () => (
  <span>{JSON.stringify(useSliceIds('bySpecies', indexes))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["dog","cat"]</span>'

store.setRow('pets', 'lowly', {species: 'worm'});
console.log(app.innerHTML);
// -> '<span>["dog","cat","worm"]</span>'

This example creates a Provider context into which a default Indexes object is provided. A component within it then uses the useSliceIds hook.

import {Provider, useSliceIds} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useSliceIds('bySpecies'))}</span>;

const indexes = createIndexes(
  createStore().setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  }),
).setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<span>["dog","cat"]</span>'

This example creates a Provider context into which a default Indexes object is provided. A component within it then uses the useSliceIds hook.

import {Provider, useSliceIds} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexesById={{petIndexes: indexes}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useSliceIds('bySpecies', 'petIndexes'))}</span>
);

const indexes = createIndexes(
  createStore().setTable('pets', {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'dog'},
  }),
).setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<span>["dog","cat"]</span>'
Since

v1.0.0

useSliceIdsListener

The useSliceIdsListener hook registers a listener function with the Indexes object that will be called whenever the Slice Ids in an Index change.

useSliceIdsListener(
  indexId: IdOrNull,
  listener: SliceIdsListener,
  listenerDeps?: DependencyList,
  indexesOrIndexesId?: IndexesOrIndexesId,
): void
TypeDescription
indexIdIdOrNull

The Id of the Index to listen to, or null as a wildcard.

listenerSliceIdsListener

The function that will be called whenever the Slice Ids in the Index change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

indexesOrIndexesId?IndexesOrIndexesId

The Indexes object to register the listener with: omit for the default context Indexes object, provide an Id for a named context Indexes object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useSliceIds hook).

You can either listen to a single Index (by specifying the Index Id as the method's first parameter), or changes to any Index (by providing a null wildcard).

Unlike the addSliceIdsListener method, which returns a listener Id and requires you to remove it manually, the useSliceIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Indexes object will be deleted.

Example

This example uses the useSliceIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Indexes object.

import {Provider, useSliceIdsListener} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useSliceIdsListener('bySpecies', () => console.log('Slice Ids changed'));
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App indexes={indexes} />);
console.log(indexes.getListenerStats().sliceIds);
// -> 1

store.setRow('pets', 'lowly', {species: 'worm'});
// -> 'Slice Ids changed'

root.unmount();
console.log(indexes.getListenerStats().sliceIds);
// -> 0
Since

v1.0.0

Metrics hooks

This is the collection of metrics hooks within the ui-react module. There are 8 metrics hooks in total.

useCreateMetrics

The useCreateMetrics hook is used to create a Metrics object within a React application with convenient memoization.

useCreateMetrics(
  store: undefined | Store,
  create: (store: Store) => Metrics,
  createDeps?: DependencyList,
): Metrics | undefined
TypeDescription
storeundefined | Store

A reference to the Store for which to create a new Metrics object.

create(store: Store) => Metrics

A function for performing the creation steps of the Metrics object for the Store, plus any additional steps such as adding definitions or listeners, and returning it.

createDeps?DependencyList

An optional array of dependencies for the create function, which, if any change, result in its rerun. This parameter defaults to an empty array.

returnsMetrics | undefined

A reference to the Metrics object.

It is possible to create a Metrics object outside of the React app with the regular createMetrics function and pass it in, but you may prefer to create it within the app, perhaps inside the top-level component. To prevent a new Metrics object being created every time the app renders or re-renders, since v5.0 this hook performs the creation in an effect. As a result it will return undefined on the brief first render (or if the Store is not yet defined), which you should defend against.

If your create function contains other dependencies, the changing of which should also cause the Metrics object to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

This hook ensures the Metrics object is destroyed whenever a new one is created or the component is unmounted.

Examples

This example creates a Metrics object at the top level of a React application. Even though the App component is rendered twice, the Metrics object creation only occurs once by default.

import {createMetrics, createStore} from 'tinybase';
import {useCreateMetrics, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store = useCreateStore(() =>
    createStore().setTable('species', {dog: {price: 5}, cat: {price: 4}}),
  );
  const metrics = useCreateMetrics(store, (store) => {
    console.log('Metrics created');
    return createMetrics(store).setMetricDefinition(
      'speciesCount',
      'species',
    );
  });
  return <span>{metrics?.getMetric('speciesCount')}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Metrics created'

root.render(<App />);
// No second Metrics creation

console.log(app.innerHTML);
// -> '<span>2</span>'

This example creates a Metrics object at the top level of a React application. The App component is rendered twice, each with a different top-level prop. The useCreateMetrics hook takes the tableToCount prop as a dependency, and so the Metrics object is created again on the second render.

import {createMetrics, createStore} from 'tinybase';
import {useCreateMetrics, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({tableToCount}) => {
  const store = useCreateStore(() =>
    createStore()
      .setTable('pets', {fido: {species: 'dog'}})
      .setTable('species', {dog: {price: 5}, cat: {price: 4}}),
  );
  const metrics = useCreateMetrics(
    store,
    (store) => {
      console.log(`Count created for ${tableToCount} table`);
      return createMetrics(store).setMetricDefinition(
        'tableCount',
        tableToCount,
      );
    },
    [tableToCount],
  );
  return <span>{metrics?.getMetric('tableCount')}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App tableToCount="pets" />);
// -> 'Count created for pets table'

console.log(app.innerHTML);
// -> '<span>1</span>'

root.render(<App tableToCount="species" />);
// -> 'Count created for species table'

console.log(app.innerHTML);
// -> '<span>2</span>'
Since

v1.0.0

useMetric

The useMetric hook gets the current value of a Metric, and registers a listener so that any changes to that result will cause a re-render.

useMetric(
  metricId: string,
  metricsOrMetricsId?: MetricsOrMetricsId,
): number | undefined
TypeDescription
metricIdstring

The Id of the Metric.

metricsOrMetricsId?MetricsOrMetricsId

The Metrics object to be accessed: omit for the default context Metrics object, provide an Id for a named context Metrics object, or provide an explicit reference.

returnsnumber | undefined

The numeric value of the Metric, or undefined.

A Provider component is used to wrap part of an application in a context, and it can contain a default Metrics object or a set of Metrics objects named by Id. The useMetric hook lets you indicate which Metrics object to get data for: omit the optional final parameter for the default context Metrics object, provide an Id for a named context Metrics object, or provide a Metrics object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Metric will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Metrics object outside the application, which is used in the useMetric hook by reference. A change to the Metric re-renders the component.

import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useMetric} from 'tinybase/ui-react';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});
const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
const App = () => <span>{useMetric('highestPrice', metrics)}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>5</span>'

store.setCell('species', 'horse', 'price', 20);
console.log(app.innerHTML);
// -> '<span>20</span>'

This example creates a Provider context into which a default Metrics object is provided. A component within it then uses the useMetric hook.

import {Provider, useMetric} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({metrics}) => (
  <Provider metrics={metrics}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useMetric('highestPrice')}</span>;

const metrics = createMetrics(
  createStore().setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
    worm: {price: 1},
  }),
).setMetricDefinition('highestPrice', 'species', 'max', 'price');

const app = document.createElement('div');
createRoot(app).render(<App metrics={metrics} />);
console.log(app.innerHTML);
// -> '<span>5</span>'

This example creates a Provider context into which a default Metrics object is provided. A component within it then uses the useMetric hook.

import {Provider, useMetric} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({metrics}) => (
  <Provider metricsById={{petMetrics: metrics}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useMetric('highestPrice', 'petMetrics')}</span>;

const metrics = createMetrics(
  createStore().setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
    worm: {price: 1},
  }),
).setMetricDefinition('highestPrice', 'species', 'max', 'price');

const app = document.createElement('div');
createRoot(app).render(<App metrics={metrics} />);
console.log(app.innerHTML);
// -> '<span>5</span>'
Since

v1.0.0

useMetricIds

The useMetricIds hook gets an array of the Metric Ids registered with a Metrics object, and registers a listener so that any changes to that result will cause a re-render.

useMetricIds(metricsOrMetricsId?: MetricsOrMetricsId): Ids
TypeDescription
metricsOrMetricsId?MetricsOrMetricsId

The Metrics object to be accessed: omit for the default context Metrics object, provide an Id for a named context Metrics object, or provide an explicit reference.

returnsIds

The Metric Ids in the Metrics object, or an empty array.

A Provider component is used to wrap part of an application in a context, and it can contain a default Metrics object or a set of Metrics objects named by Id. The useMetricIds hook lets you indicate which Metrics object to get data for: omit the optional final parameter for the default context Metrics object, provide an Id for a named context Metrics object, or provide a Metrics object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Metric Ids in the Metrics object will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Example

This example creates an Metrics object outside the application, which is used in the useMetricIds hook by reference. A newly-registered Metric re-renders the component.

import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useMetricIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const metrics = createMetrics(store);
const App = () => <span>{JSON.stringify(useMetricIds(metrics))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>[]</span>'

const addMetricDefinition = () =>
  metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
addMetricDefinition();
console.log(app.innerHTML);
// -> '<span>["highestPrice"]</span>'
Since

v4.1.0

useMetricListener

The useMetricListener hook registers a listener function with the Metrics object that will be called whenever the value of a specified Metric changes.

useMetricListener(
  metricId: IdOrNull,
  listener: MetricListener,
  listenerDeps?: DependencyList,
  metricsOrMetricsId?: MetricsOrMetricsId,
): void
TypeDescription
metricIdIdOrNull

The Id of the Metric to listen to, or null as a wildcard.

listenerMetricListener

The function that will be called whenever the Metric changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

metricsOrMetricsId?MetricsOrMetricsId

The Metrics object to register the listener with: omit for the default context Metrics object, provide an Id for a named context Metrics object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useMetric hook).

You can either listen to a single Metric (by specifying the Metric Id as the method's first parameter), or changes to any Metric (by providing a null wildcard).

Unlike the addMetricListener method, which returns a listener Id and requires you to remove it manually, the useMetricListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Metrics object, will be deleted.

Example

This example uses the useMetricListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Metrics object.

import {Provider, useMetricListener} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({metrics}) => (
  <Provider metrics={metrics}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useMetricListener('highestPrice', () => console.log('Metric changed'));
  return <span>App</span>;
};

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});
const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App metrics={metrics} />);
console.log(metrics.getListenerStats().metric);
// -> 1

store.setCell('species', 'horse', 'price', 20);
// -> 'Metric changed'

root.unmount();
console.log(metrics.getListenerStats().metric);
// -> 0
Since

v1.0.0

useMetrics

The useMetrics hook is used to get a reference to a Metrics object from within a Provider component context.

useMetrics(id?: string): Metrics | undefined
TypeDescription
id?string

An optional Id for accessing a Metrics object that was named with an Id in the Provider.

returnsMetrics | undefined

A reference to the Metrics object (or undefined if not within a Provider context, or if the requested Metrics object does not exist).

A Provider component is used to wrap part of an application in a context. It can contain a default Metrics object (or a set of Metrics objects named by Id) that can be easily accessed without having to be passed down as props through every component.

The useMetrics hook lets you either get a reference to the default Metrics object (when called without a parameter), or one of the Metrics objects that are named by Id (when called with an Id parameter).

Examples

This example creates a Provider context into which a default Metrics object is provided. A component within it then uses the useMetrics hook to get a reference to the Metrics object again, without the need to have it passed as a prop.

import {Provider, useMetrics} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({metrics}) => (
  <Provider metrics={metrics}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useMetrics().getListenerStats().metric}</span>;

const metrics = createMetrics(createStore());
const app = document.createElement('div');
createRoot(app).render(<App metrics={metrics} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Metrics object is provided, named by Id. A component within it then uses the useMetrics hook with that Id to get a reference to the Metrics object again, without the need to have it passed as a prop.

import {Provider, useMetrics} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({metrics}) => (
  <Provider metricsById={{petStore: metrics}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useMetrics('petStore').getListenerStats().metric}</span>
);

const metrics = createMetrics(createStore());
const app = document.createElement('div');
createRoot(app).render(<App metrics={metrics} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v1.0.0

useMetricsIds

The useMetricsIds hook is used to retrieve the Ids of all the named Metrics objects present in the current Provider component context.

useMetricsIds(): Ids
returnsIds

A list of the Ids in the context.

Example

This example adds two named Metrics objects to a Provider context and an inner component accesses their Ids.

import {
  Provider,
  useCreateMetrics,
  useCreateStore,
  useMetricsIds,
} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store1 = useCreateStore(createStore);
  const metrics1 = useCreateMetrics(store1, createMetrics);
  const store2 = useCreateStore(createStore);
  const metrics2 = useCreateMetrics(store2, createMetrics);
  return (
    <Provider metricsById={{metrics1, metrics2}}>
      <Pane />
    </Provider>
  );
};
const Pane = () => <span>{JSON.stringify(useMetricsIds())}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["metrics1","metrics2"]</span>'
Since

v4.1.0

useMetricsOrMetricsById

The useMetricsOrMetricsById hook is used to get a reference to a Metrics object from within a Provider component context, or have it passed directly to this hook.

useMetricsOrMetricsById(metricsOrMetricsId?: MetricsOrMetricsId): Metrics | undefined
TypeDescription
metricsOrMetricsId?MetricsOrMetricsId

Either an Id for accessing a Metrics object that was named with an Id in the Provider, or the Metrics object itself.

returnsMetrics | undefined

A reference to the Metrics object (or undefined if not within a Provider context, or if the requested Metrics object does not exist).

This is mostly of use when you are developing a component that needs a Metrics object and which might have been passed in explicitly to the component or is to be picked up from the context by Id (a common pattern for Metrics-based components).

This hook is unlikely to be used often. For most situations, you will want to use the useMetrics hook.

Example

This example creates a Provider context into which a default Metrics object is provided. A component within it then uses the useMetricsOrMetricsById hook to get a reference to the Metrics object again, without the need to have it passed as a prop. Note however, that unlike the useMetrics hook example, this component would also work if you were to pass the Metrics object directly into it, making it more portable.

import {Provider, useMetricsOrMetricsById} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({metrics}) => (
  <Provider metrics={metrics}>
    <Pane />
  </Provider>
);
const Pane = ({metrics}) => (
  <span>
    {JSON.stringify(useMetricsOrMetricsById(metrics).getMetricIds())}
  </span>
);

const metrics = createMetrics(
  createStore().setTable('species', {
    dog: {price: 5},
    cat: {price: 4},
    worm: {price: 1},
  }),
).setMetricDefinition('highestPrice', 'species', 'max', 'price');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App metrics={metrics} />);
console.log(app.innerHTML);
// -> '<span>["highestPrice"]</span>'
Since

v4.1.0

useProvideMetrics

The useProvideMetrics hook is used to add a Metrics object by Id to a Provider component, but imperatively from a component within it.

useProvideMetrics(
  metricsId: string,
  metrics: Metrics,
): void
TypeDescription
metricsIdstring

The Id of the Metrics object to be registered with the Provider.

metricsMetrics

The Metrics object to be registered.

returnsvoid

This has no return value.

Normally you will register a Metrics object by Id in a context by using the metricsById prop of the top-level Provider component. This hook, however, lets you dynamically add a new Metrics object to the context, from within a descendent component. This is useful for applications where the set of Metrics objects is not known at the time of the first render of the root Provider.

A Metrics object added to the Provider context in this way will be available to other components within the context (using the useMetrics hook and so on). If you use the same Id as an existing Metrics object registration, the new one will take priority over one provided by the metricsById prop.

Note that other components that consume a Metrics object registered like this should defend against it being undefined at first. On the first render, the other component will likely not yet have completed the registration. In the example below, we use the null-safe useMetrics('petMetrics')? to do this.

Example

This example creates a Provider context. A child component registers a Metrics object into it which is then consumable by a peer child component.

import {
  Provider,
  useCreateMetrics,
  useCreateStore,
  useMetrics,
  useProvideMetrics,
} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => (
  <Provider>
    <RegisterMetrics />
    <ConsumeMetrics />
  </Provider>
);
const RegisterMetrics = () => {
  const store = useCreateStore(() =>
    createStore().setCell('pets', 'fido', 'color', 'brown'),
  );
  const metrics = useCreateMetrics(store, (store) =>
    createMetrics(store).setMetricDefinition('petCount', 'pets', 'count'),
  );
  useProvideMetrics('petMetrics', metrics);
  return null;
};
const ConsumeMetrics = () => (
  <span>{useMetrics('petMetrics')?.getMetric('petCount')}</span>
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>1</span>'
Since

v5.3.0

Persister hooks

This is the collection of persister hooks within the ui-react module. There are 6 persister hooks in total.

useCreatePersister

The useCreatePersister hook is used to create a Persister within a React application along with convenient memoization and callbacks.

useCreatePersister<Persist, PersisterOrUndefined>(
  store: undefined | PersistedStore<Persist>,
  create: (store: PersistedStore<Persist>) => PersisterOrUndefined | Promise<PersisterOrUndefined>,
  createDeps?: DependencyList,
  then?: (persister: Persister<Persist>) => Promise<void>,
  thenDeps?: DependencyList,
  destroy?: (persister: Persister<Persist>) => void,
  destroyDeps?: DependencyList,
): PersisterOrUndefined
TypeDescription
storeundefined | PersistedStore<Persist>

A reference to the Store for which to create a new Persister object.

create(store: PersistedStore<Persist>) => PersisterOrUndefined | Promise<PersisterOrUndefined>

A (possibly asynchronous) function for performing the creation steps of the Persister object for the Store.

createDeps?DependencyList

An optional array of dependencies for the create function, which, if any change, result in its rerun. This parameter defaults to an empty array.

then?(persister: Persister<Persist>) => Promise<void>

An optional callback for performing asynchronous post-creation steps on the Persister, such as starting automatic loading and saving.

thenDeps?DependencyList

An optional array of dependencies for the then callback, which, if any change, result in its rerun. This parameter defaults to an empty array.

destroy?(persister: Persister<Persist>) => void

An optional callback whenever the Persister is destroyed due to a change in the createDeps dependencies.

destroyDeps?DependencyList

An optional array of dependencies for the destroy callback, which, if any change, result in destroy and then being rerun. This parameter defaults to an empty array.

returnsPersisterOrUndefined

A reference to the Persister.

It is possible to create a Persister outside of the React app with the regular createPersister function and pass it in, but you may prefer to create it within the app, perhaps inside the top-level component. To prevent a new Persister being created every time the app renders or re-renders, since v5.0 the this hook performs the creation in an effect.

If your create function (the second parameter to the hook) contains dependencies, the changing of which should cause the Persister to be recreated, you can provide them in an array in the third parameter, just as you would for any React hook with dependencies. The Store passed in as the first parameter of this hook is used as a dependency by default.

A second callback, called then, can be provided as the fourth parameter. This is called after the creation, and, importantly, can be asynchronous, so that you can configure the Persister with the startAutoLoad method and startAutoSave method, for example. If this callback contains dependencies, the changing of which should cause the Persister to be reconfigured, you can provide them in an array in the fifth parameter. The Persister itself is used as a dependency by default.

See the note below about possible future deprecation of the then callback, however.

Since v4.3.0, the create function can return undefined, meaning that you can enable or disable persistence conditionally within this hook. This is useful for applications which might turn on or off their cloud persistence or collaboration features. This hook can return undefined if the Store is not yet defined, which you should defend against.

Since v4.3.19, a destroy function can be provided which will be called after an old Persister is destroyed due to a change in the createDeps dependencies that causes a new one to be created. Use this to clean up any underlying storage objects that you set up during the then function, for example. If this callback itself contains additional dependencies, you can provide them in an array in the seventh parameter.

Since v5.2, the create function can be asynchronous, which now makes it a suitable place to call the Persister's startAutoLoad and startAutoSave methods. At some major version in the future, the then parameter will be removed, since that only really existed to perform such asynchronous initial tasks.

This hook ensures the Persister object is destroyed whenever a new one is created or the component is unmounted.

Examples

This example creates a Persister at the top level of a React application. Even though the App component is rendered twice, the Persister creation only occurs once by default.

import {
  useCreatePersister,
  useCreateStore,
  useTables,
} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = () => {
  const store = useCreateStore(createStore);
  useCreatePersister(
    store,
    (store) => {
      console.log('Persister created');
      return createSessionPersister(store, 'pets');
    },
    [],
    async (persister) => {
      await persister.startAutoLoad();
      await persister.startAutoSave();
    },
  );
  return <span>{JSON.stringify(useTables(store))}</span>;
};

sessionStorage.setItem(
  'pets',
  '[{"pets":{"fido":{"species":"dog"}}}, {}]',
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Persister created'

// ...
root.render(<App />);
// No second Persister creation

console.log(app.innerHTML);
// -> '<span>{\"pets\":{\"fido\":{\"species\":\"dog\"}}}</span>'

root.unmount();

This example creates a Persister at the top level of a React application. The App component is rendered twice, each with a different top-level prop. The useCreatePersister hook takes the sessionKey prop as a dependency, and so the Persister object is created again on the second render. The first is destroyed and the destroy parameter is called for it.

import {
  useCreatePersister,
  useCreateStore,
  useTables,
} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = ({sessionKey}) => {
  const store = useCreateStore(createStore);
  useCreatePersister(
    store,
    (store) => {
      console.log(`Persister created for session key ${sessionKey}`);
      return createSessionPersister(store, sessionKey);
    },
    [sessionKey],
    async (persister) => {
      await persister.startAutoLoad();
    },
    [],
    (persister) =>
      console.log(
        `Persister destroyed for session key ${persister.getStorageName()}`,
      ),
  );
  return <span>{JSON.stringify(useTables(store))}</span>;
};

sessionStorage.setItem(
  'fidoStore',
  '[{"pets":{"fido":{"species":"dog"}}}, {}]',
);
sessionStorage.setItem(
  'cujoStore',
  '[{"pets":{"cujo":{"species":"dog"}}}, {}]',
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App sessionKey="fidoStore" />);
// -> 'Persister created for session key fidoStore'

// ...
console.log(app.innerHTML);
// -> '<span>{\"pets\":{\"fido\":{\"species\":\"dog\"}}}</span>'

root.render(<App sessionKey="cujoStore" />);
// -> 'Persister created for session key cujoStore'
// -> 'Persister destroyed for session key fidoStore'

// ...
console.log(app.innerHTML);
// -> '<span>{\"pets\":{\"cujo\":{\"species\":\"dog\"}}}</span>'

root.unmount();
// -> 'Persister destroyed for session key cujoStore'
Since

v1.0.0

usePersister

The usePersister hook is used to get a reference to a Persister object from within a Provider component context.

usePersister(id?: string): AnyPersister | undefined
TypeDescription
id?string

An optional Id for accessing a Persister object that was named with an Id in the Provider.

returnsAnyPersister | undefined

A reference to the Persister object (or undefined if not within a Provider context, or if the requested Persister object does not exist).

A Provider component is used to wrap part of an application in a context. It can contain a default Persister object (or a set of Persister objects named by Id) that can be easily accessed without having to be passed down as props through every component.

The usePersister hook lets you either get a reference to the default Persister object (when called without a parameter), or one of the Persister objects that are named by Id (when called with an Id parameter).

Examples

This example creates a Provider context into which a default Persister object is provided. A component within it then uses the usePersister hook to get a reference to the Persister object again, without the need to have it passed as a prop.

import {Provider, usePersister} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = ({persister}) => (
  <Provider persister={persister}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{usePersister().getStatus()}</span>;

const persister = createSessionPersister(createStore(), 'pets');
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App persister={persister} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Persister object is provided, named by Id. A component within it then uses the usePersister hook with that Id to get a reference to the Persister object again, without the need to have it passed as a prop.

import {Provider, usePersister} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = ({persister}) => (
  <Provider persistersById={{petPersister: persister}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{usePersister('petPersister').getStatus()}</span>;

const persister = createSessionPersister(createStore(), 'pets');
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App persister={persister} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v5.3.0

usePersisterIds

The usePersisterIds hook is used to retrieve the Ids of all the named Persister objects present in the current Provider component context.

usePersisterIds(): Ids
returnsIds

A list of the Ids in the context.

Example

This example adds two named Persister objects to a Provider context and an inner component accesses their Ids.

import {
  Provider,
  useCreatePersister,
  useCreateStore,
  usePersisterIds,
} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = () => {
  const store1 = useCreateStore(createStore);
  const persister1 = useCreatePersister(store1, (store1) =>
    createSessionPersister(store1, 'pets1'),
  );
  const store2 = useCreateStore(createStore);
  const persister2 = useCreatePersister(store2, (store2) =>
    createSessionPersister(store2, 'pets2'),
  );
  return (
    <Provider persistersById={{persister1, persister2}}>
      <Pane />
    </Provider>
  );
};
const Pane = () => <span>{JSON.stringify(usePersisterIds())}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);

// ...
console.log(app.innerHTML);
// -> '<span>["persister1","persister2"]</span>'
Since

v5.3.0

usePersisterOrPersisterById

The usePersisterOrPersisterById hook is used to get a reference to a Persister object from within a Provider component context, or have it passed directly to this hook.

usePersisterOrPersisterById(persisterOrPersisterId?: PersisterOrPersisterId): AnyPersister | undefined
TypeDescription
persisterOrPersisterId?PersisterOrPersisterId

Either an Id for accessing a Persister object that was named with an Id in the Provider, or the Persister object itself.

returnsAnyPersister | undefined

A reference to the Persister object (or undefined if not within a Provider context, or if the requested Persister object does not exist).

This is mostly of use when you are developing a component that needs a Persister object and which might have been passed in explicitly to the component or is to be picked up from the context by Id (a common pattern for Persister-based components).

This is unlikely to be used often. For most situations, you will want to use the usePersister hook.

Example

This example creates a Provider context into which a default Persister object is provided. A component within it then uses the usePersisterOrPersisterById hook to get a reference to the Persister object again, without the need to have it passed as a prop. Note however, that unlike the usePersister hook example, this component would also work if you were to pass the Persister object directly into it, making it more portable.

import {Provider, usePersisterOrPersisterById} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = ({persister}) => (
  <Provider persister={persister}>
    <Pane />
  </Provider>
);
const Pane = ({persister}) => (
  <span>{usePersisterOrPersisterById(persister).getStatus()}</span>
);

const persister = createSessionPersister(createStore(), 'pets');
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App persister={persister} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v5.3.0

usePersisterStatus

The usePersisterStatus hook returns a the status of a Persister, and registers a listener so that any changes to it will cause a re-render.

usePersisterStatus(persisterOrPersisterId?: PersisterOrPersisterId): Status
TypeDescription
persisterOrPersisterId?PersisterOrPersisterId

The Persister to be accessed: omit for the default context Persister, provide an Id for a named context Persister, or provide an explicit reference.

returnsStatus

The status of the Persister: 0 means idle, 1 means loading, and 2 means saving.

A Provider component is used to wrap part of an application in a context, and it can contain a default Persister or a set of Persister objects named by Id. The usePersisterStatus hook lets you indicate which Persister to get data for: omit the optional parameter for the default context Persister, provide an Id for a named context Persister, or provide a Persister explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Persister status will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Persister outside the application, which is used in the usePersisterStatus hook by reference. A change to the status of the Persister re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';
import {usePersisterStatus} from 'tinybase/ui-react';

const persister = createSessionPersister(createStore(), 'pets');
const App = () => <span>{usePersisterStatus(persister)}</span>;

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a default Persister is provided. A component within it then uses the usePersisterStatus hook.

import {Provider, usePersisterStatus} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = ({persister}) => (
  <Provider persister={persister}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{usePersisterStatus()}</span>;

const persister = createSessionPersister(createStore(), 'pets');
const app = document.createElement('div');
createRoot(app).render(<App persister={persister} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Persister is provided, named by Id. A component within it then uses the usePersisterStatus hook.

import {Provider, usePersisterStatus} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = ({persister}) => (
  <Provider persistersById={{petPersister: persister}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{usePersisterStatus('petPersister')}</span>;

const persister = createSessionPersister(createStore(), 'pets');
const app = document.createElement('div');
createRoot(app).render(<App persister={persister} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v5.3.0

usePersisterStatusListener

The usePersisterStatusListener hook registers a listener function with the Persister that will be called when its status changes.

usePersisterStatusListener(
  listener: StatusListener<StoreOrMergeableStore>,
  listenerDeps?: DependencyList,
  persisterOrPersisterId?: PersisterOrPersisterId,
): void
TypeDescription
listenerStatusListener<StoreOrMergeableStore>

The function that will be called whenever the status of the Persister changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

persisterOrPersisterId?PersisterOrPersisterId

The Persister to be accessed: omit for the default context Persister, provide an Id for a named context Persister, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the usePersisterStatus hook).

Unlike the addStatusListener method, which returns a listener Id and requires you to remove it manually, the usePersisterStatusListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Persister will be deleted.

Example

This example uses the usePersisterStatusListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Persister.

import {Provider, usePersisterStatusListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createSessionPersister} from 'tinybase/persisters/persister-browser';
import {createStore} from 'tinybase';

const App = ({persister}) => (
  <Provider persister={persister}>
    <Pane />
  </Provider>
);
const Pane = () => {
  usePersisterStatusListener((persister, status) =>
    console.log('Persister status changed: ' + status),
  );
  return <span>App</span>;
};

const persister = createSessionPersister(createStore(), 'pets');
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App persister={persister} />);

persister.load();
// -> 'Persister status changed: 1'
// ...
// -> 'Persister status changed: 0'

persister.save();
// -> 'Persister status changed: 2'
// ...
// -> 'Persister status changed: 0'
Since

v5.3.0

Queries hooks

This is the collection of queries hooks within the ui-react module. There are 21 queries hooks in total.

useResultTable

The useResultTable hook returns an object containing the entire data of the ResultTable of the given query, and registers a listener so that any changes to that result will cause a re-render.

useResultTable(
  queryId: string,
  queriesOrQueriesId?: QueriesOrQueriesId,
): Table
TypeDescription
queryIdstring

The Id of the query.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsTable

An object containing the entire data of the ResultTable.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useResultTable hook lets you indicate which Queries object to get data for: omit the final optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the query result will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Queries object outside the application, which is used in the useTable hook by reference. A change to the data in the query re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useResultTable} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);
const App = () => (
  <span>{JSON.stringify(useResultTable('dogColors', queries))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>{"fido":{"color":"brown"},"cujo":{"color":"black"}}</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>{"fido":{"color":"walnut"},"cujo":{"color":"black"}}</span>'

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useResultTable hook.

import {Provider, useResultTable} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useResultTable('dogColors'))}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});

const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>{"fido":{"color":"brown"},"cujo":{"color":"black"}}</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useResultTable hook.

import {Provider, useResultTable} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useResultTable('dogColors', 'petQueries'))}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>{"fido":{"color":"brown"},"cujo":{"color":"black"}}</span>'
Since

v2.0.0

useResultTableCellIds

The useResultTableCellIds hook returns the Ids of every Cell used across the whole ResultTable of the given query, and registers a listener so that any changes to those Ids will cause a re-render.

useResultTableCellIds(
  queryId: string,
  queriesOrQueriesId?: QueriesOrQueriesId,
): Ids
TypeDescription
queryIdstring

The Id of the query.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference. See the addResultTableCellIdsListener method for more details.

returnsIds

An array of the Ids of every Cell in the result of the query.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useResultTableCellIds hook lets you indicate which Queries object to get data for: omit the final optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the result Cell Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Queries object outside the application, which is used in the useResultTableCellIds hook by reference. A change to the data in the query re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useResultTableCellIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'dogColorsAndLegs',
  'pets',
  ({select, where}) => {
    select('color');
    select('legs');
    where('species', 'dog');
  },
);
const App = () => (
  <span>
    {JSON.stringify(useResultTableCellIds('dogColorsAndLegs', queries))}
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["color"]</span>'

store.setCell('pets', 'cujo', 'legs', 4);
console.log(app.innerHTML);
// -> '<span>["color","legs"]</span>'

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useResultTableCellIds hook.

import {Provider, useResultTableCellIds} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useResultTableCellIds('dogColorsAndLegs'))}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black', legs: 4},
  }),
).setQueryDefinition('dogColorsAndLegs', 'pets', ({select, where}) => {
  select('color');
  select('legs');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["color","legs"]</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useResultTableCellIds hook.

import {Provider, useResultTableCellIds} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(
      useResultTableCellIds('dogColorsAndLegs', 'petQueries'),
    )}
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black', legs: 4},
  }),
).setQueryDefinition('dogColorsAndLegs', 'pets', ({select, where}) => {
  select('color');
  select('legs');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["color","legs"]</span>'
Since

v4.1.0

useResultTableCellIdsListener

The useResultTableCellIdsListener hook registers a listener function with a Queries object that will be called whenever the Cell Ids that appear anywhere in a ResultTable change.

useResultTableCellIdsListener(
  queryId: IdOrNull,
  listener: ResultTableCellIdsListener,
  listenerDeps?: DependencyList,
  queriesOrQueriesId?: QueriesOrQueriesId,
): void
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

listenerResultTableCellIdsListener

The function that will be called whenever the Cell Ids that appear anywhere in the ResultTable change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to register the listener with: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useResultTableCellIds hook).

You can either listen to a single ResultTable (by specifying a query Id as the method's first parameter) or changes to any ResultTable (by providing a null wildcard).

Unlike the addResultTableCellIdsListener method, which returns a listener Id and requires you to remove it manually, the useResultTableCellIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Queries object will be deleted.

Example

This example uses the useResultTableCellIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Queries object.

import {Provider, useResultTableCellIdsListener} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useResultTableCellIdsListener('petColorsAndLegs', () =>
    console.log('Result Cell Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColorsAndLegs',
  'pets',
  ({select}) => {
    select('color');
    select('legs');
  },
);
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(queries.getListenerStats().tableCellIds);
// -> 1

store.setCell('pets', 'cujo', 'legs', 4);
// -> 'Result Cell Ids changed'

root.unmount();
console.log(queries.getListenerStats().tableCellIds);
// -> 0
Since

v4.1.0

useResultTableListener

The useResultTableListener hook registers a listener function with a Queries object that will be called whenever data in a ResultTable changes.

useResultTableListener(
  queryId: IdOrNull,
  listener: ResultTableListener,
  listenerDeps?: DependencyList,
  queriesOrQueriesId?: QueriesOrQueriesId,
): void
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

listenerResultTableListener

The function that will be called whenever data in the matching ResultTable changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to register the listener with: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useResultTable hook).

You can either listen to a single ResultTable (by specifying a query Id as the method's first parameter) or changes to any ResultTable (by providing a null wildcard).

Unlike the addResultTableListener method, which returns a listener Id and requires you to remove it manually, the useResultTableListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Queries object will be deleted.

Example

This example uses the useResultTableListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Queries object.

import {Provider, useResultTableListener} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useResultTableListener('petColors', () =>
    console.log('Result table changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(queries.getListenerStats().table);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Result table changed'

root.unmount();
console.log(queries.getListenerStats().table);
// -> 0
Since

v2.0.0

useResultRowIds

The useResultRowIds hook returns the Ids of every Row in the ResultTable of the given query, and registers a listener so that any changes to those Ids will cause a re-render.

useResultRowIds(
  queryId: string,
  queriesOrQueriesId?: QueriesOrQueriesId,
): Ids
TypeDescription
queryIdstring

The Id of the query.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference. See the addResultRowIdsListener method for more details.

returnsIds

An array of the Ids of every Row in the result of the query.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useResultRowIds hook lets you indicate which Queries object to get data for: omit the final optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the result Row Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Queries object outside the application, which is used in the useResultRowIds hook by reference. A change to the data in the query re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useResultRowIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);
const App = () => (
  <span>{JSON.stringify(useResultRowIds('dogColors', queries))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'

store.setCell('pets', 'cujo', 'species', 'wolf');
console.log(app.innerHTML);
// -> '<span>["fido"]</span>'

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useResultRowIds hook.

import {Provider, useResultRowIds} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useResultRowIds('dogColors'))}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useResultRowIds hook.

import {Provider, useResultRowIds} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useResultRowIds('dogColors', 'petQueries'))}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'
Since

v2.0.0

useResultRowIdsListener

The useResultRowIdsListener hook registers a listener function with a Queries object that will be called whenever the Row Ids in a ResultTable change.

useResultRowIdsListener(
  queryId: IdOrNull,
  listener: ResultRowIdsListener,
  listenerDeps?: DependencyList,
  queriesOrQueriesId?: QueriesOrQueriesId,
): void
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

listenerResultRowIdsListener

The function that will be called whenever the Row Ids in the matching ResultTable change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to register the listener with: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useResultRowIds hook).

You can either listen to a single ResultTable (by specifying a query Id as the method's first parameter) or changes to any ResultTable (by providing a null wildcard).

Unlike the addResultRowIdsListener method, which returns a listener Id and requires you to remove it manually, the useResultRowIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Queries object will be deleted.

Example

This example uses the useResultRowIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Queries object.

import {Provider, useResultRowIdsListener} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useResultRowIdsListener('petColors', () =>
    console.log('Result Row Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(queries.getListenerStats().rowIds);
// -> 1

store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
// -> 'Result Row Ids changed'

root.unmount();
console.log(queries.getListenerStats().rowIds);
// -> 0
Since

v2.0.0

useResultSortedRowIds

The useResultSortedRowIds hook returns the sorted (and optionally, paginated) Ids of every Row in the ResultTable of the given query, and registers a listener so that any changes to those Ids will cause a re-render.

useResultSortedRowIds(
  queryId: string,
  cellId?: string,
  descending?: boolean,
  offset?: number,
  limit?: number,
  queriesOrQueriesId?: QueriesOrQueriesId,
): Ids
TypeDescription
queryIdstring

The Id of the query.

cellId?string

The Id of the result Cell whose values are used for the sorting, or undefined to by sort the result Row Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of Row Ids to skip for pagination purposes, if any.

limit?number

The maximum number of Row Ids to return, or undefined for all.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsIds

An array of the Ids of every Row in the result of the query.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useResultSortedRowIds hook lets you indicate which Queries object to get data for: omit the final optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the sorted result Row Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Queries object outside the application, which is used in the useResultSortedRowIds hook by reference. A change to the data in the query re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useResultSortedRowIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);
const App = () => (
  <span>
    {JSON.stringify(
      useResultSortedRowIds(
        'dogColors',
        'color',
        false,
        0,
        undefined,
        queries,
      ),
    )}
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["cujo","fido"]</span>'

store.setCell('pets', 'cujo', 'species', 'wolf');
console.log(app.innerHTML);
// -> '<span>["fido"]</span>'

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useResultSortedRowIds hook.

import {Provider, useResultSortedRowIds} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(useResultSortedRowIds('dogColors', 'color'))}
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["cujo","fido"]</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useResultSortedRowIds hook.

import {Provider, useResultSortedRowIds} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(
      useResultSortedRowIds(
        'dogColors',
        'color',
        false,
        0,
        undefined,
        'petQueries',
      ),
    )}
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["cujo","fido"]</span>'
Since

v2.0.0

useResultSortedRowIdsListener

The useResultSortedRowIdsListener hook registers a listener function with a Queries object that will be called whenever the sorted (and optionally, paginated) Row Ids in a ResultTable change.

useResultSortedRowIdsListener(
  queryId: string,
  cellId: undefined | string,
  descending: boolean,
  offset: number,
  limit: undefined | number,
  listener: ResultSortedRowIdsListener,
  listenerDeps?: DependencyList,
  queriesOrQueriesId?: QueriesOrQueriesId,
): void
TypeDescription
queryIdstring

The Id of the query to listen to.

cellIdundefined | string

The Id of the Cell whose values are used for the sorting, or undefined to by sort the Row Id itself.

descendingboolean

Whether the sorting should be in descending order.

offsetnumber

The number of Row Ids to skip for pagination purposes, if any.

limitundefined | number

The maximum number of Row Ids to return, or undefined for all.

listenerResultSortedRowIdsListener

The function that will be called whenever the Row Ids in the matching ResultTable change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to register the listener with: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useResultSortedRowIds hook).

Unlike the addResultSortedRowIdsListener method, which returns a listener Id and requires you to remove it manually, the useResultSortedRowIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Queries object will be deleted.

Example

This example uses the useResultSortedRowIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Queries object.

import {Provider, useResultSortedRowIdsListener} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useResultSortedRowIdsListener(
    'petColors',
    'color',
    false,
    0,
    undefined,
    () => console.log('Sorted result Row Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(queries.getListenerStats().sortedRowIds);
// -> 1

store.setRow('pets', 'cujo', {color: 'tan'});
// -> 'Sorted result Row Ids changed'

root.unmount();
console.log(queries.getListenerStats().sortedRowIds);
// -> 0
Since

v2.0.0

useResultRow

The useResultRow hook returns an object containing the data of a single Row in the ResultTable of the given query, and registers a listener so that any changes to that Row will cause a re-render.

useResultRow(
  queryId: string,
  rowId: string,
  queriesOrQueriesId?: QueriesOrQueriesId,
): Row
TypeDescription
queryIdstring

The Id of the query.

rowIdstring

The Id of the Row in the ResultTable.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsRow

An object containing the entire data of the Row in the ResultTable of the query.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useResultRow hook lets you indicate which Queries object to get data for: omit the final optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the result Row will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Queries object outside the application, which is used in the useResultRow hook by reference. A change to the data in the query re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useResultRow} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);
const App = () => (
  <span>{JSON.stringify(useResultRow('dogColors', 'fido', queries))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>{"color":"brown"}</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>{"color":"walnut"}</span>'

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useResultRow hook.

import {Provider, useResultRow} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useResultRow('dogColors', 'fido'))}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>{"color":"brown"}</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useResultRow hook.

import {Provider, useResultRow} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(useResultRow('dogColors', 'fido', 'petQueries'))}
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>{"color":"brown"}</span>'
Since

v2.0.0

useResultRowCount

The useResultRowCount hook returns the count of the Row objects in the ResultTable of the given query, and registers a listener so that any changes to that result will cause a re-render.

useResultRowCount(
  queryId: string,
  queriesOrQueriesId?: QueriesOrQueriesId,
): number
TypeDescription
queryIdstring

The Id of the query.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference. See the addResultRowCountListener method for more details.

returnsnumber

The number of ResultRow objects in the ResultTable.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useResultRowCount hook lets you indicate which Queries object to get data for: omit the final optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the count of ResultRow objects will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Queries object outside the application, which is used in the useResultRowCount hook by reference. A change to the data in the query re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useResultRowCount} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('color');
    where('species', 'dog');
  },
);
const App = () => <span>{useResultRowCount('dogColors', queries)}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>2</span>'

store.setCell('pets', 'cujo', 'species', 'wolf');
console.log(app.innerHTML);
// -> '<span>1</span>'

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useResultRowCount hook.

import {Provider, useResultRowCount} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useResultRowCount('dogColors')}</span>;

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>2</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useResultRowCount hook.

import {Provider, useResultRowCount} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useResultRowCount('dogColors', 'petQueries')}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>2</span>'
Since

v4.1.0

useResultRowCountListener

The useResultRowCountListener hook registers a listener function with a Queries object that will be called whenever the count of ResultRow objects in a ResultTable changes.

useResultRowCountListener(
  queryId: IdOrNull,
  listener: ResultRowCountListener,
  listenerDeps?: DependencyList,
  queriesOrQueriesId?: QueriesOrQueriesId,
): void
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

listenerResultRowCountListener

The function that will be called whenever the Row Ids in the matching ResultTable change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to register the listener with: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useResultRowCount hook).

You can either listen to a single ResultTable (by specifying a query Id as the method's first parameter) or changes to any ResultTable (by providing a null wildcard).

Unlike the addResultRowCountListener method, which returns a listener Id and requires you to remove it manually, the useResultRowCountListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Queries object will be deleted.

Example

This example uses the useResultRowCountListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Queries object.

import {Provider, useResultRowCountListener} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useResultRowCountListener('petColors', () =>
    console.log('Result Row count changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(queries.getListenerStats().rowCount);
// -> 1

store.setRow('pets', 'rex', {species: 'dog', color: 'tan'});
// -> 'Result Row count changed'

root.unmount();
console.log(queries.getListenerStats().rowCount);
// -> 0
Since

v4.1.0

useResultRowListener

The useResultRowListener hook registers a listener function with a Queries object that will be called whenever data in a result Row changes.

useResultRowListener(
  queryId: IdOrNull,
  rowId: IdOrNull,
  listener: ResultRowListener,
  listenerDeps?: DependencyList,
  queriesOrQueriesId?: QueriesOrQueriesId,
): void
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the result Row to listen to, or null as a wildcard.

listenerResultRowListener

The function that will be called whenever data in the matching result Row changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to register the listener with: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useResultRow hook).

You can either listen to a single result Row (by specifying a query Id and Row Id as the method's first two parameters) or changes to any result Row (by providing null wildcards).

Both, either, or neither of the queryId and rowId parameters can be wildcarded with null. You can listen to a specific result Row in a specific query, any result Row in a specific query, a specific result Row in any query, or any result Row in any query.

Unlike the addResultRowListener method, which returns a listener Id and requires you to remove it manually, the useResultRowListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Queries object will be deleted.

Example

This example uses the useResultRowListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Queries object.

import {Provider, useResultRowListener} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useResultRowListener('petColors', 'fido', () =>
    console.log('Result row changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(queries.getListenerStats().row);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Result row changed'

root.unmount();
console.log(queries.getListenerStats().row);
// -> 0
Since

v2.0.0

useResultCellIds

The useResultCellIds hook returns the Ids of every Cell in a given Row in the ResultTable of the given query, and registers a listener so that any changes to those Ids will cause a re-render.

useResultCellIds(
  queryId: string,
  rowId: string,
  queriesOrQueriesId?: QueriesOrQueriesId,
): Ids
TypeDescription
queryIdstring

The Id of the query.

rowIdstring

The Id of the Row in the ResultTable.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsIds

An array of the Ids of every Cell in the Row in the result of the query.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useResultCellIds hook lets you indicate which Queries object to get data for: omit the final optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the result Cell Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Queries object outside the application, which is used in the useResultCellIds hook by reference. A change to the data in the query re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useResultCellIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('species');
    select('color');
    select('legs');
    where('species', 'dog');
  },
);
const App = () => (
  <span>
    {JSON.stringify(useResultCellIds('dogColors', 'fido', queries))}
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["species","color"]</span>'

store.setCell('pets', 'fido', 'legs', 4);
console.log(app.innerHTML);
// -> '<span>["species","color","legs"]</span>'

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useResultCellIds hook.

import {Provider, useResultCellIds} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useResultCellIds('dogColors', 'fido'))}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('species');
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["species","color"]</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useResultCellIds hook.

import {Provider, useResultCellIds} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(useResultCellIds('dogColors', 'fido', 'petQueries'))}
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('species');
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["species","color"]</span>'
Since

v2.0.0

useResultCellIdsListener

The useResultCellIdsListener hook registers a listener function with a Queries object that will be called whenever the Cell Ids in a result Row change.

useResultCellIdsListener(
  queryId: IdOrNull,
  rowId: IdOrNull,
  listener: ResultCellIdsListener,
  listenerDeps?: DependencyList,
  queriesOrQueriesId?: QueriesOrQueriesId,
): void
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the result Row to listen to, or null as a wildcard.

listenerResultCellIdsListener

The function that will be called whenever the Row Ids in the matching ResultTable change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to register the listener with: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useResultCellIds hook).

Both, either, or neither of the queryId and rowId parameters can be wildcarded with null. You can listen to a specific result Row in a specific query, any result Row in a specific query, a specific result Row in any query, or any result Row in any query.

Unlike the addResultCellIdsListener method, which returns a listener Id and requires you to remove it manually, the useResultCellIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Queries object will be deleted.

Example

This example uses the useResultCellIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Queries object.

import {Provider, useResultCellIdsListener} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useResultCellIdsListener('petColors', 'fido', () =>
    console.log('Result cell Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => {
    select('color');
    select('legs');
  },
);
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(queries.getListenerStats().cellIds);
// -> 1

store.setCell('pets', 'fido', 'legs', 4);
// -> 'Result cell Ids changed'

root.unmount();
console.log(queries.getListenerStats().cellIds);
// -> 0
Since

v2.0.0

useResultCell

The useResultCell hook returns the value of a single Cell in a given Row in the ResultTable of the given query, and registers a listener so that any changes to that value will cause a re-render.

useResultCell(
  queryId: string,
  rowId: string,
  cellId: string,
  queriesOrQueriesId?: QueriesOrQueriesId,
): Cell | undefined
TypeDescription
queryIdstring

The Id of the query.

rowIdstring

The Id of the Row in the ResultTable.

cellIdstring

The Id of the Cell in the Row.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsCell | undefined

The value of the Cell, or undefined.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useResultCell hook lets you indicate which Queries object to get data for: omit the final optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the result Cell will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Queries object outside the application, which is used in the useResultCell hook by reference. A change to the data in the query re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useResultCell} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'dogColors',
  'pets',
  ({select, where}) => {
    select('species');
    select('color');
    select('legs');
    where('species', 'dog');
  },
);
const App = () => (
  <span>{useResultCell('dogColors', 'fido', 'color', queries)}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>brown</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>walnut</span>'

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useResultCell hook.

import {Provider, useResultCell} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useResultCell('dogColors', 'fido', 'color')}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('species');
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>brown</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useResultCell hook.

import {Provider, useResultCell} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useResultCell('dogColors', 'fido', 'color', 'petQueries')}</span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('species');
  select('color');
  where('species', 'dog');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>brown</span>'
Since

v2.0.0

useResultCellListener

The useResultCellListener hook registers a listener function with a Queries object that will be called whenever data in a Cell changes.

useResultCellListener(
  queryId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: ResultCellListener,
  listenerDeps?: DependencyList,
  queriesOrQueriesId?: QueriesOrQueriesId,
): void
TypeDescription
queryIdIdOrNull

The Id of the query to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the result Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the result Cell to listen to, or null as a wildcard.

listenerResultCellListener

The function that will be called whenever data in the matching result Cell changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

queriesOrQueriesId?QueriesOrQueriesId

The Queries object to register the listener with: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useResultCell hook).

You can either listen to a single Cell (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or changes to any Cell (by providing null wildcards).

All, some, or none of the queryId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific result Row in a specific query, any Cell in any result Row in any query, for example - or every other combination of wildcards.

Unlike the addResultCellListener method, which returns a listener Id and requires you to remove it manually, the useResultCellListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Queries object will be deleted.

Example

This example uses the useResultCellListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Queries object.

import {Provider, useResultCellListener} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useResultCellListener('petColors', 'fido', 'color', () =>
    console.log('Result cell changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(queries.getListenerStats().cell);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Result cell changed'

root.unmount();
console.log(queries.getListenerStats().cell);
// -> 0
Since

v2.0.0

useCreateQueries

The useCreateQueries hook is used to create a Queries object within a React application with convenient memoization.

useCreateQueries(
  store: undefined | Store,
  create: (store: Store) => Queries,
  createDeps?: DependencyList,
): Queries | undefined
TypeDescription
storeundefined | Store

A reference to the Store for which to create a new Queries object.

create(store: Store) => Queries

An optional callback for performing post-creation steps on the Queries object, such as adding definitions or listeners.

createDeps?DependencyList

An optional array of dependencies for the create function, which, if any change, result in its rerun. This parameter defaults to an empty array.

returnsQueries | undefined

A reference to the Queries object.

It is possible to create a Queries object outside of the React app with the regular createQueries function and pass it in, but you may prefer to create it within the app, perhaps inside the top-level component. To prevent a new Queries object being created every time the app renders or re-renders, since v5.0 this hook performs the creation in an effect. As a result it will return undefined on the brief first render (or if the Store is not yet defined), which you should defend against.

If your create function contains other dependencies, the changing of which should also cause the Queries object to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

This hook ensures the Queries object is destroyed whenever a new one is created or the component is unmounted.

Examples

This example creates a Queries object at the top level of a React application. Even though the App component is rendered twice, the Queries object creation only occurs once by default.

import {createQueries, createStore} from 'tinybase';
import {useCreateQueries, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store = useCreateStore(() =>
    createStore().setTable('pets', {
      fido: {species: 'dog', color: 'brown'},
      felix: {species: 'cat', color: 'black'},
      cujo: {species: 'dog', color: 'black'},
    }),
  );
  const queries = useCreateQueries(store, (store) => {
    console.log('Queries created');
    return createQueries(store).setQueryDefinition(
      'dogColors',
      'pets',
      ({select, where}) => {
        select('color');
        where('species', 'dog');
      },
    );
  });
  return (
    <span>{queries?.getResultCell('dogColors', 'fido', 'color')}</span>
  );
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Queries created'

root.render(<App />);
// No second Queries creation

console.log(app.innerHTML);
// -> '<span>brown</span>'

This example creates a Queries object at the top level of a React application. The App component is rendered twice, each with a different top-level prop. The useCreateQueries hook takes the resultCell prop as a dependency, and so the Queries object is created again on the second render.

import {createQueries, createStore} from 'tinybase';
import {useCreateQueries, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store = useCreateStore(() =>
    createStore().setTable('pets', {
      fido: {species: 'dog', color: 'brown'},
      felix: {species: 'cat', color: 'black'},
      cujo: {species: 'dog', color: 'black'},
    }),
  );
  const queries = useCreateQueries(store, (store) => {
    console.log('Queries created');
    return createQueries(store).setQueryDefinition(
      'dogColors',
      'pets',
      ({select, where}) => {
        select('color');
        where('species', 'dog');
      },
    );
  });
  return (
    <span>{queries?.getResultCell('dogColors', 'fido', 'color')}</span>
  );
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Queries created'

root.render(<App />);
// No second Queries creation

console.log(app.innerHTML);
// -> '<span>brown</span>'
Since

v2.0.0

useQueries

The useQueries hook is used to get a reference to a Queries object from within a Provider component context.

useQueries(id?: string): Queries | undefined
TypeDescription
id?string

An optional Id for accessing a Queries object that was named with an Id in the Provider.

returnsQueries | undefined

A reference to the Queries object (or undefined if not within a Provider context, or if the requested Queries object does not exist).

A Provider component is used to wrap part of an application in a context. It can contain a default Queries object (or a set of Queries objects named by Id) that can be easily accessed without having to be passed down as props through every component.

The useQueries hook lets you either get a reference to the default Queries object (when called without a parameter), or one of the Queries objects that are named by Id (when called with an Id parameter).

Examples

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useQueries hook to get a reference to the Queries object again, without the need to have it passed as a prop.

import {Provider, useQueries} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useQueries().getListenerStats().table}</span>;

const queries = createQueries(createStore());
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Queries object is provided, named by Id. A component within it then uses the useQueries hook with that Id to get a reference to the Queries object again, without the need to have it passed as a prop.

import {Provider, useQueries} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queriesById={{petQueries: queries}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useQueries('petQueries').getListenerStats().table}</span>
);

const queries = createQueries(createStore());
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v2.0.0

useQueriesIds

The useQueriesIds hook is used to retrieve the Ids of all the named Queries objects present in the current Provider component context.

useQueriesIds(): Ids
returnsIds

A list of the Ids in the context.

Example

This example adds two named Queries objects to a Provider context and an inner component accesses their Ids.

import {
  Provider,
  useCreateQueries,
  useCreateStore,
  useQueriesIds,
} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store1 = useCreateStore(createStore);
  const queries1 = useCreateQueries(store1, createQueries);
  const store2 = useCreateStore(createStore);
  const queries2 = useCreateQueries(store2, createQueries);
  return (
    <Provider queriesById={{queries1, queries2}}>
      <Pane />
    </Provider>
  );
};
const Pane = () => <span>{JSON.stringify(useQueriesIds())}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["queries1","queries2"]</span>'
Since

v4.1.0

useQueriesOrQueriesById

The useQueriesOrQueriesById hook is used to get a reference to a Queries object from within a Provider component context, or have it passed directly to this hook.

useQueriesOrQueriesById(queriesOrQueriesId?: QueriesOrQueriesId): Queries | undefined
TypeDescription
queriesOrQueriesId?QueriesOrQueriesId

Either an Id for accessing a Queries object that was named with an Id in the Provider, or the Queries object itself.

returnsQueries | undefined

A reference to the Queries object (or undefined if not within a Provider context, or if the requested Queries object does not exist).

This is mostly of use when you are developing a component that needs a Queries object and which might have been passed in explicitly to the component or is to be picked up from the context by Id (a common pattern for Queries-based components).

This is unlikely to be used often. For most situations, you will want to use the useQueries hook.

Example

This example creates a Provider context into which a default Queries object is provided. A component within it then uses the useQueriesOrQueriesById hook to get a reference to the Queries object again, without the need to have it passed as a prop. Note however, that unlike the useQueries hook example, this component would also work if you were to pass the Queries object directly into it, making it more portable.

import {Provider, useQueriesOrQueriesById} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = ({queries}) => (
  <span>
    {JSON.stringify(useQueriesOrQueriesById(queries).getQueryIds())}
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('dogColors', 'pets', ({select, where}) => {
  select('color');
  where('species', 'dog');
});

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>["dogColors"]</span>'
Since

v4.1.0

useQueryIds

The useQueryIds hook gets an array of the Query Ids registered with a Queries object, and registers a listener so that any changes to that result will cause a re-render.

useQueryIds(queriesOrQueriesId?: QueriesOrQueriesId): Ids
TypeDescription
queriesOrQueriesId?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

returnsIds

The Query Ids in the Queries object, or an empty array.

A Provider component is used to wrap part of an application in a context, and it can contain a default Queries object or a set of Queries objects named by Id. The useQueryIds hook lets you indicate which Queries object to get data for: omit the optional final parameter for the default context Queries object, provide an Id for a named context Queries object, or provide a Queries object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Query Ids in the Queries object will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Example

This example creates an Queries object outside the application, which is used in the useQueryIds hook by reference. A newly-registered Relationship re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useQueryIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const queries = createQueries(store);
const App = () => <span>{JSON.stringify(useQueryIds(queries))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>[]</span>'

const addQueryDefinition = () =>
  queries.setQueryDefinition('dogColors', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  });
addQueryDefinition();
console.log(app.innerHTML);
// -> '<span>["dogColors"]</span>'
Since

v4.1.0

Relationships hooks

This is the collection of relationships hooks within the ui-react module. There are 11 relationships hooks in total.

useLinkedRowIds

The useLinkedRowIds hook gets the linked Row Ids for a given Row in a linked list Relationship, and registers a listener so that any changes to that result will cause a re-render.

useLinkedRowIds(
  relationshipId: string,
  firstRowId: string,
  relationshipsOrRelationshipsId?: RelationshipsOrRelationshipsId,
): Ids
TypeDescription
relationshipIdstring

The Id of the Relationship.

firstRowIdstring

The Id of the first Row in the linked list Relationship.

relationshipsOrRelationshipsId?RelationshipsOrRelationshipsId

The Relationships object to be accessed: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

returnsIds

The linked Row Ids in the Relationship.

A Provider component is used to wrap part of an application in a context, and it can contain a default Relationships object or a set of Relationships objects named by Id. The useLinkedRowIds hook lets you indicate which Relationships object to get data for: omit the optional final parameter for the default context Relationships object, provide an Id for a named context Relationships object, or provide a Relationships object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the linked Row Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Relationships object outside the application, which is used in the useLinkedRowIds hook by reference. A change to the linked Row Ids re-renders the component.

import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useLinkedRowIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', next: 'felix'},
  felix: {species: 'cat', next: 'cujo'},
  cujo: {species: 'dog'},
});
const relationships = createRelationships(store).setRelationshipDefinition(
  'petSequence',
  'pets',
  'pets',
  'next',
);
const App = () => (
  <span>
    {JSON.stringify(useLinkedRowIds('petSequence', 'fido', relationships))}
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["fido","felix","cujo"]</span>'

store.setRow('pets', 'toto', {species: 'dog'});
store.setCell('pets', 'cujo', 'next', 'toto');
console.log(app.innerHTML);
// -> '<span>["fido","felix","cujo","toto"]</span>'

This example creates a Provider context into which a default Relationships object is provided. A component within it then uses the useLinkedRowIds hook.

import {Provider, useLinkedRowIds} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useLinkedRowIds('petSequence', 'fido'))}</span>
);

const relationships = createRelationships(
  createStore().setTable('pets', {
    fido: {species: 'dog', next: 'felix'},
    felix: {species: 'cat', next: 'cujo'},
    cujo: {species: 'dog'},
  }),
).setRelationshipDefinition('petSequence', 'pets', 'pets', 'next');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>["fido","felix","cujo"]</span>'

This example creates a Provider context into which a default Relationships object is provided. A component within it then uses the useLinkedRowIds hook.

import {Provider, useLinkedRowIds} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationshipsById={{petRelationships: relationships}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(
      useLinkedRowIds('petSequence', 'fido', 'petRelationships'),
    )}
  </span>
);

const relationships = createRelationships(
  createStore().setTable('pets', {
    fido: {species: 'dog', next: 'felix'},
    felix: {species: 'cat', next: 'cujo'},
    cujo: {species: 'dog'},
  }),
).setRelationshipDefinition('petSequence', 'pets', 'pets', 'next');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>["fido","felix","cujo"]</span>'
Since

v1.0.0

useLinkedRowIdsListener

The useLinkedRowIdsListener hook registers a listener function with the Relationships object that will be called whenever the linked Row Ids in a Relationship change.

useLinkedRowIdsListener(
  relationshipId: string,
  firstRowId: string,
  listener: LinkedRowIdsListener,
  listenerDeps?: DependencyList,
  relationshipsOrRelationshipsId?: RelationshipsOrRelationshipsId,
): void
TypeDescription
relationshipIdstring

The Id of the Relationship to listen to.

firstRowIdstring

The Id of the first Row of the linked list to listen to.

listenerLinkedRowIdsListener

The function that will be called whenever the linked Row Ids change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

relationshipsOrRelationshipsId?RelationshipsOrRelationshipsId

The Relationships object to register the listener with: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useLinkedRowsId hook).

Unlike other listener registration methods, you cannot provide null wildcards for the first two parameters of the useLinkedRowIdsListener method. This prevents the prohibitive expense of tracking all the possible linked lists (and partial linked lists within them) in a Store.

Unlike the addLinkedRowsIdListener method, which returns a listener Id and requires you to remove it manually, the useLinkedRowIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Indexes object will be deleted.

Example

This example uses the useLinkedRowIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Relationships object.

import {Provider, useLinkedRowIdsListener} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useLinkedRowIdsListener('petSequence', 'fido', () =>
    console.log('Linked Row Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', next: 'felix'},
  felix: {species: 'cat', next: 'cujo'},
  cujo: {species: 'dog'},
});
const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSequence',
  'pets',
  'pets',
  'next',
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(relationships.getListenerStats().linkedRowIds);
// -> 1

store.setRow('pets', 'toto', {species: 'dog'});
store.setCell('pets', 'cujo', 'next', 'toto');
// -> 'Linked Row Ids changed'

root.unmount();
console.log(relationships.getListenerStats().linkedRowIds);
// -> 0
Since

v1.0.0

useLocalRowIds

The useLocalRowIds hook gets the local Row Ids for a given remote Row in a Relationship, and registers a listener so that any changes to that result will cause a re-render.

useLocalRowIds(
  relationshipId: string,
  remoteRowId: string,
  relationshipsOrRelationshipsId?: RelationshipsOrRelationshipsId,
): Ids
TypeDescription
relationshipIdstring

The Id of the Relationship.

remoteRowIdstring

The Id of the remote Row in the Relationship.

relationshipsOrRelationshipsId?RelationshipsOrRelationshipsId

The Relationships object to be accessed: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

returnsIds

The local Row Ids in the Relationship, or an empty array.

A Provider component is used to wrap part of an application in a context, and it can contain a default Relationships object or a set of Relationships objects named by Id. The useLocalRowIds hook lets you indicate which Relationships object to get data for: omit the optional final parameter for the default context Relationships object, provide an Id for a named context Relationships object, or provide a Relationships object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the local Row Id will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Relationships object outside the application, which is used in the useLocalRowIds hook by reference. A change to the local Row Ids re-renders the component.

import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useLocalRowIds} from 'tinybase/ui-react';

const store = createStore()
  .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
  .setTable('species', {wolf: {price: 10}, dog: {price: 5}});
const relationships = createRelationships(store).setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);
const App = () => (
  <span>
    {JSON.stringify(useLocalRowIds('petSpecies', 'dog', relationships))}
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'

store.setRow('pets', 'toto', {species: 'dog'});
console.log(app.innerHTML);
// -> '<span>["fido","cujo","toto"]</span>'

This example creates a Provider context into which a default Relationships object is provided. A component within it then uses the useLocalRowIds hook.

import {Provider, useLocalRowIds} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useLocalRowIds('petSpecies', 'dog'))}</span>
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'

This example creates a Provider context into which a default Relationships object is provided. A component within it then uses the useLocalRowIds hook.

import {Provider, useLocalRowIds} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationshipsById={{petRelationships: relationships}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(
      useLocalRowIds('petSpecies', 'dog', 'petRelationships'),
    )}
  </span>
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>["fido","cujo"]</span>'
Since

v1.0.0

useLocalRowIdsListener

The useLocalRowIdsListener hook registers a listener function with the Relationships object that will be called whenever the local Row Ids in a Relationship change.

useLocalRowIdsListener(
  relationshipId: IdOrNull,
  remoteRowId: IdOrNull,
  listener: LocalRowIdsListener,
  listenerDeps?: DependencyList,
  relationshipsOrRelationshipsId?: RelationshipsOrRelationshipsId,
): void
TypeDescription
relationshipIdIdOrNull

The Id of the Relationship to listen to, or null as a wildcard.

remoteRowIdIdOrNull

The Id of the remote Row to listen to, or null as a wildcard.

listenerLocalRowIdsListener

The function that will be called whenever the local Row Ids change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

relationshipsOrRelationshipsId?RelationshipsOrRelationshipsId

The Relationships object to register the listener with: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useLocalRowsId hook).

You can either listen to a single local Row (by specifying the Relationship Id and local Row Id as the method's first two parameters), or changes to any local Row (by providing a null wildcards).

Both, either, or neither of the relationshipId and remoteRowId parameters can be wildcarded with null. You can listen to a specific remote Row in a specific Relationship, any remote Row in a specific Relationship, a specific remote Row in any Relationship, or any remote Row in any Relationship.

Unlike the addLocalRowsIdListener method, which returns a listener Id and requires you to remove it manually, the useLocalRowIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Indexes object will be deleted.

Example

This example uses the useLocalRowIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Relationships object.

import {Provider, useLocalRowIdsListener} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useLocalRowIdsListener('petSpecies', 'dog', () =>
    console.log('Local Row Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore()
  .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
  .setTable('species', {wolf: {price: 10}, dog: {price: 5}});
const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(relationships.getListenerStats().localRowIds);
// -> 1

store.setRow('pets', 'toto', {species: 'dog'});
// -> 'Local Row Ids changed'

root.unmount();
console.log(relationships.getListenerStats().localRowIds);
// -> 0
Since

v1.0.0

useRemoteRowId

The useRemoteRowId hook gets the remote Row Id for a given local Row in a Relationship, and registers a listener so that any changes to that result will cause a re-render.

useRemoteRowId(
  relationshipId: string,
  localRowId: string,
  relationshipsOrRelationshipsId?: RelationshipsOrRelationshipsId,
): Id | undefined
TypeDescription
relationshipIdstring

The Id of the Relationship.

localRowIdstring

The Id of the local Row in the Relationship.

relationshipsOrRelationshipsId?RelationshipsOrRelationshipsId

The Relationships object to be accessed: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

returnsId | undefined

The remote Row Id in the Relationship, or undefined.

A Provider component is used to wrap part of an application in a context, and it can contain a default Relationships object or a set of Relationships objects named by Id. The useRemoteRowId hook lets you indicate which Relationships object to get data for: omit the optional final parameter for the default context Relationships object, provide an Id for a named context Relationships object, or provide a Relationships object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the remote Row Id will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Relationships object outside the application, which is used in the useRemoteRowId hook by reference. A change to the remote Row Id re-renders the component.

import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useRemoteRowId} from 'tinybase/ui-react';

const store = createStore()
  .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
  .setTable('species', {wolf: {price: 10}, dog: {price: 5}});
const relationships = createRelationships(store).setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);
const App = () => (
  <span>{useRemoteRowId('petSpecies', 'cujo', relationships)}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>dog</span>'

store.setCell('pets', 'cujo', 'species', 'wolf');
console.log(app.innerHTML);
// -> '<span>wolf</span>'

This example creates a Provider context into which a default Relationships object is provided. A component within it then uses the useRemoteRowId hook.

import {Provider, useRemoteRowId} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useRemoteRowId('petSpecies', 'cujo')}</span>;

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>dog</span>'

This example creates a Provider context into which a default Relationships object is provided. A component within it then uses the useRemoteRowId hook.

import {Provider, useRemoteRowId} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationshipsById={{petRelationships: relationships}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useRemoteRowId('petSpecies', 'cujo', 'petRelationships')}</span>
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>dog</span>'
Since

v1.0.0

useRemoteRowIdListener

The useRemoteRowIdListener hook registers a listener function with the Relationships object that will be called whenever a remote Row Id in a Relationship changes.

useRemoteRowIdListener(
  relationshipId: IdOrNull,
  localRowId: IdOrNull,
  listener: RemoteRowIdListener,
  listenerDeps?: DependencyList,
  relationshipsOrRelationshipsId?: RelationshipsOrRelationshipsId,
): void
TypeDescription
relationshipIdIdOrNull

The Id of the Relationship to listen to, or null as a wildcard.

localRowIdIdOrNull

The Id of the local Row to listen to, or null as a wildcard.

listenerRemoteRowIdListener

The function that will be called whenever the remote Row Id changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

relationshipsOrRelationshipsId?RelationshipsOrRelationshipsId

The Relationships object to register the listener with: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useRemoteRowId hook).

You can either listen to a single local Row (by specifying the Relationship Id and local Row Id as the method's first two parameters), or changes to any local Row (by providing a null wildcards).

Both, either, or neither of the relationshipId and localRowId parameters can be wildcarded with null. You can listen to a specific local Row in a specific Relationship, any local Row in a specific Relationship, a specific local Row in any Relationship, or any local Row in any Relationship.

Unlike the addRemoteRowIdListener method, which returns a listener Id and requires you to remove it manually, the useRemoteRowIdListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Indexes object will be deleted.

Example

This example uses the useRemoteRowIdListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Relationships object.

import {Provider, useRemoteRowIdListener} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useRemoteRowIdListener('petSpecies', 'cujo', () =>
    console.log('Remote Row Id changed'),
  );
  return <span>App</span>;
};

const store = createStore()
  .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
  .setTable('species', {wolf: {price: 10}, dog: {price: 5}});
const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(relationships.getListenerStats().remoteRowId);
// -> 1

store.setCell('pets', 'cujo', 'species', 'wolf');
// -> 'Remote Row Id changed'

root.unmount();
console.log(relationships.getListenerStats().remoteRowId);
// -> 0
Since

v1.0.0

useCreateRelationships

The useCreateRelationships hook is used to create a Relationships object within a React application with convenient memoization.

useCreateRelationships(
  store: undefined | Store,
  create: (store: Store) => Relationships,
  createDeps?: DependencyList,
): Relationships | undefined
TypeDescription
storeundefined | Store

A reference to the Store for which to create a new Relationships object.

create(store: Store) => Relationships

An optional callback for performing post-creation steps on the Relationships object, such as adding definitions or listeners.

createDeps?DependencyList

An optional array of dependencies for the create function, which, if any change, result in its rerun. This parameter defaults to an empty array.

returnsRelationships | undefined

A reference to the Relationships object.

It is possible to create a Relationships object outside of the React app with the regular createRelationships function and pass it in, but you may prefer to create it within the app, perhaps inside the top-level component. To prevent a new Relationships object being created every time the app renders or re-renders, since v5.0 this hook performs the creation in an effect. As a result it will return undefined on the brief first render (or if the Store is not yet defined), which you should defend against.

If your create function contains other dependencies, the changing of which should also cause the Relationships object to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

This hook ensures the Relationships object is destroyed whenever a new one is created or the component is unmounted.

Examples

This example creates a Relationships object at the top level of a React application. Even though the App component is rendered twice, the Relationships object creation only occurs once by default.

import {createRelationships, createStore} from 'tinybase';
import {useCreateRelationships, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store = useCreateStore(() =>
    createStore()
      .setTable('pets', {
        fido: {species: 'dog'},
        felix: {species: 'cat'},
        cujo: {species: 'dog'},
      })
      .setTable('species', {dog: {price: 5}, cat: {price: 4}}),
  );
  const relationships = useCreateRelationships(store, (store) => {
    console.log('Relationships created');
    return createRelationships(store).setRelationshipDefinition(
      'petSpecies',
      'pets',
      'species',
      'species',
    );
  });
  return (
    <span>{relationships?.getRemoteRowId('petSpecies', 'fido')}</span>
  );
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Relationships created'

root.render(<App />);
// No second Relationships creation

console.log(app.innerHTML);
// -> '<span>dog</span>'

This example creates a Relationships object at the top level of a React application. The App component is rendered twice, each with a different top-level prop. The useCreateRelationships hook takes the remoteTableAndCellToLink prop as a dependency, and so the Relationships object is created again on the second render.

import {createRelationships, createStore} from 'tinybase';
import {useCreateRelationships, useCreateStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({remoteTableAndCellToLink}) => {
  const store = useCreateStore(() =>
    createStore()
      .setTable('pets', {
        fido: {species: 'dog', color: 'brown'},
        felix: {species: 'cat', color: 'black'},
        cujo: {species: 'dog', color: 'brown'},
      })
      .setTable('species', {dog: {price: 5}, cat: {price: 4}})
      .setTable('color', {brown: {discount: 0.1}, black: {discount: 0}}),
  );
  const relationships = useCreateRelationships(
    store,
    (store) => {
      console.log(`Relationship created to ${remoteTableAndCellToLink}`);
      return createRelationships(store).setRelationshipDefinition(
        'cellLinked',
        'pets',
        remoteTableAndCellToLink,
        remoteTableAndCellToLink,
      );
    },
    [remoteTableAndCellToLink],
  );
  return (
    <span>{relationships?.getRemoteRowId('cellLinked', 'fido')}</span>
  );
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App remoteTableAndCellToLink="species" />);
// -> 'Relationship created to species'

console.log(app.innerHTML);
// -> '<span>dog</span>'

root.render(<App remoteTableAndCellToLink="color" />);
// -> 'Relationship created to color'

console.log(app.innerHTML);
// -> '<span>brown</span>'
Since

v1.0.0

useRelationshipIds

The useRelationshipIds hook gets an array of the Relationship Ids registered with a Relationships object, and registers a listener so that any changes to that result will cause a re-render.

useRelationshipIds(relationshipsOrRelationshipsId?: RelationshipsOrRelationshipsId): Ids
TypeDescription
relationshipsOrRelationshipsId?RelationshipsOrRelationshipsId

The Relationships object to be accessed: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

returnsIds

The Relationship Ids in the Relationships object, or an empty array.

A Provider component is used to wrap part of an application in a context, and it can contain a default Relationships object or a set of Relationships objects named by Id. The useRelationshipIds hook lets you indicate which Relationships object to get data for: omit the optional final parameter for the default context Relationships object, provide an Id for a named context Relationships object, or provide a Relationships object explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Relationship Ids in the Relationships object will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Example

This example creates an Relationships object outside the application, which is used in the useRelationshipIds hook by reference. A newly-registered Relationship re-renders the component.

import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {useRelationshipIds} from 'tinybase/ui-react';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const relationships = createRelationships(store);
const App = () => (
  <span>{JSON.stringify(useRelationshipIds(relationships))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>[]</span>'

const addRelationshipDefinition = () =>
  relationships.setRelationshipDefinition(
    'petSpecies',
    'pets',
    'species',
    'species',
  );
addRelationshipDefinition();
console.log(app.innerHTML);
// -> '<span>["petSpecies"]</span>'
Since

v4.1.0

useRelationships

The useRelationships hook is used to get a reference to a Relationships object from within a Provider component context.

useRelationships(id?: string): Relationships | undefined
TypeDescription
id?string

An optional Id for accessing a Relationships object that was named with an Id in the Provider.

returnsRelationships | undefined

A reference to the Relationships object (or undefined if not within a Provider context, or if the requested Relationships object does not exist).

A Provider component is used to wrap part of an application in a context. It can contain a default Relationships object (or a set of Relationships objects named by Id) that can be easily accessed without having to be passed down as props through every component.

The useRelationships hook lets you either get a reference to the default Relationships object (when called without a parameter), or one of the Relationships objects that are named by Id (when called with an Id parameter).

Examples

This example creates a Provider context into which a default Relationships object is provided. A component within it then uses the useRelationships hook to get a reference to the Relationships object again, without the need to have it passed as a prop.

import {Provider, useRelationships} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useRelationships().getListenerStats().remoteRowId}</span>
);

const relationships = createRelationships(createStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Relationships object is provided, named by Id. A component within it then uses the useRelationships hook with that Id to get a reference to the Relationships object again, without the need to have it passed as a prop.

import {Provider, useRelationships} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationshipsById={{petStore: relationships}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {useRelationships('petStore').getListenerStats().remoteRowId}
  </span>
);

const relationships = createRelationships(createStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v1.0.0

useRelationshipsIds

The useRelationshipsIds hook is used to retrieve the Ids of all the named Relationships objects present in the current Provider component context.

useRelationshipsIds(): Ids
returnsIds

A list of the Ids in the context.

Example

This example adds two named Relationships objects to a Provider context and an inner component accesses their Ids.

import {
  Provider,
  useCreateRelationships,
  useCreateStore,
  useRelationshipsIds,
} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store1 = useCreateStore(createStore);
  const relationships1 = useCreateRelationships(
    store1,
    createRelationships,
  );
  const store2 = useCreateStore(createStore);
  const relationships2 = useCreateRelationships(
    store2,
    createRelationships,
  );
  return (
    <Provider relationshipsById={{relationships1, relationships2}}>
      <Pane />
    </Provider>
  );
};
const Pane = () => <span>{JSON.stringify(useRelationshipsIds())}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["relationships1","relationships2"]</span>'
Since

v4.1.0

useRelationshipsOrRelationshipsById

The useRelationshipsOrRelationshipsById hook is used to get a reference to a Relationships object from within a Provider component context, or have it passed directly to this hook.

useRelationshipsOrRelationshipsById(relationshipsOrRelationshipsId?: RelationshipsOrRelationshipsId): Relationships | undefined
TypeDescription
relationshipsOrRelationshipsId?RelationshipsOrRelationshipsId

Either an Id for accessing a Relationships object that was named with an Id in the Provider, or the Relationships object itself.

returnsRelationships | undefined

A reference to the Relationships object (or undefined if not within a Provider context, or if the requested Relationships object does not exist).

This is mostly of use when you are developing a component that needs a Relationships object and which might have been passed in explicitly to the component or is to be picked up from the context by Id (a common pattern for Relationships-based components).

This is unlikely to be used often. For most situations, you will want to use the useRelationships hook.

Example

This example creates a Provider context into which a default Relationships object is provided. A component within it then uses the useRelationshipsOrRelationshipsById hook to get a reference to the Relationships object again, without the need to have it passed as a prop. Note however, that unlike the useRelationships hook example, this component would also work if you were to pass the Relationships object directly into it, making it more portable.

import {
  Provider,
  useRelationshipsOrRelationshipsById,
} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = ({relationships}) => (
  <span>
    {JSON.stringify(
      useRelationshipsOrRelationshipsById(
        relationships,
      ).getRelationshipIds(),
    )}
  </span>
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<span>["petSpecies"]</span>'
Since

v4.1.0

Store hooks

This is the collection of store hooks within the ui-react module. There are 64 store hooks in total.

useCreateMergeableStore

The useCreateMergeableStore hook.

useCreateMergeableStore(
  create: () => MergeableStore,
  createDeps?: DependencyList,
): MergeableStore
TypeDescription
create() => MergeableStore
createDeps?DependencyList
returnsMergeableStore

Since

v1.0.0

useCreateStore

The useCreateStore hook is used to create a Store within a React application with convenient memoization.

useCreateStore(
  create: () => Store,
  createDeps?: DependencyList,
): Store
TypeDescription
create() => Store

A function for performing the creation of the Store, plus any additional steps such as adding data or listeners, and returning it.

createDeps?DependencyList

An optional array of dependencies for the create function, which, if any change, result in its rerun. This parameter defaults to an empty array.

returnsStore

A reference to the Store.

It is possible to create a Store outside of the React app with the regular createStore function and pass it in, but you may prefer to create it within the app, perhaps inside the top-level component. To prevent a new Store being created every time the app renders or re-renders, the useCreateStore hook wraps the creation in a memoization.

The useCreateStore hook is a very thin wrapper around the React useMemo hook, defaulting to an empty array for its dependencies, so that by default, the creation only occurs once.

If your create function contains other dependencies, the changing of which should cause the Store to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

Examples

This example creates an empty Store at the top level of a React application. Even though the App component is rendered twice, the Store creation only occurs once by default.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useCreateStore} from 'tinybase/ui-react';

const App = () => {
  const store = useCreateStore(() => {
    console.log('Store created');
    return createStore().setTables({pets: {fido: {species: 'dog'}}});
  });
  return <span>{store.getCell('pets', 'fido', 'species')}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Store created'

root.render(<App />);
// No second Store creation

console.log(app.innerHTML);
// -> '<span>dog</span>'

This example creates an empty Store at the top level of a React application. The App component is rendered twice, each with a different top-level prop. The useCreateStore hook takes the fidoSpecies prop as a dependency, and so the Store is created again on the second render.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useCreateStore} from 'tinybase/ui-react';

const App = ({fidoSpecies}) => {
  const store = useCreateStore(() => {
    console.log(`Store created for fido as ${fidoSpecies}`);
    return createStore().setTables({pets: {fido: {species: fidoSpecies}}});
  }, [fidoSpecies]);
  return <span>{store.getCell('pets', 'fido', 'species')}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App fidoSpecies="dog" />);
// -> 'Store created for fido as dog'

console.log(app.innerHTML);
// -> '<span>dog</span>'

root.render(<App fidoSpecies="cat" />);
// -> 'Store created for fido as cat'

console.log(app.innerHTML);
// -> '<span>cat</span>'
Since

v1.0.0

useProvideStore

The useProvideStore hook is used to add a Store object by Id to a Provider component, but imperatively from a component within it.

useProvideStore(
  storeId: string,
  store: Store,
): void
TypeDescription
storeIdstring

The Id of the Store object to be registered with the Provider.

storeStore

The Store object to be registered.

returnsvoid

This has no return value.

Normally you will register a Store by Id in a context by using the storesById prop of the top-level Provider component. This hook, however, lets you dynamically add a new Store to the context, from within a descendent component. This is useful for applications where the set of Stores is not known at the time of the first render of the root Provider.

A Store added to the Provider context in this way will be available to other components within the context (using the useStore hook and so on). If you use the same Id as an existing Store registration, the new one will take priority over one provided by the storesById prop.

Note that other components that consume a Store registered like this should defend against it being undefined at first. On the first render, the other component will likely not yet have completed the registration. In the example below, we use the null-safe useStore('petStore')? to do this.

Example

This example creates a Provider context. A child component registers a Store into it which is then consumable by a peer child component.

import {
  Provider,
  useCreateStore,
  useProvideStore,
  useStore,
} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = () => (
  <Provider>
    <RegisterStore />
    <ConsumeStore />
  </Provider>
);
const RegisterStore = () => {
  const store = useCreateStore(() =>
    createStore().setCell('pets', 'fido', 'color', 'brown'),
  );
  useProvideStore('petStore', store);
  return null;
};
const ConsumeStore = () => (
  <span>{JSON.stringify(useStore('petStore')?.getTableIds())}</span>
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>["pets"]</span>'
Since

v4.4.2

useStore

The useStore hook is used to get a reference to a Store from within a Provider component context.

useStore(id?: string): Store | undefined
TypeDescription
id?string

An optional Id for accessing a Store that was named with an Id in the Provider.

returnsStore | undefined

A reference to the Store (or undefined if not within a Provider context, or if the requested Store does not exist).

A Provider component is used to wrap part of an application in a context. It can contain a default Store (or a set of Store objects named by Id) that can be easily accessed without having to be passed down as props through every component.

The useStore hook lets you either get a reference to the default Store (when called without a parameter), or one of the Store objects that are named by Id (when called with an Id parameter).

Examples

This example creates a Provider context into which a default Store is provided. A component within it then uses the useStore hook to get a reference to the Store again, without the need to have it passed as a prop.

import {Provider, useStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useStore().getListenerStats().tables}</span>;

const store = createStore();
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useStore hook with that Id to get a reference to the Store again, without the need to have it passed as a prop.

import {Provider, useStore} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useStore('petStore').getListenerStats().tables}</span>
);

const store = createStore();
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v1.0.0

useStoreIds

The useStoreIds hook is used to retrieve the Ids of all the named Store objects present in the current Provider component context.

useStoreIds(): Ids
returnsIds

A list of the Ids in the context.

Example

This example adds two named Store objects to a Provider context and an inner component accesses their Ids.

import {Provider, useCreateStore, useStoreIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = () => {
  const store1 = useCreateStore(createStore);
  const store2 = useCreateStore(createStore);
  return (
    <Provider storesById={{store1, store2}}>
      <Pane />
    </Provider>
  );
};
const Pane = () => <span>{JSON.stringify(useStoreIds())}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["store1","store2"]</span>'
Since

v4.1.0

useStoreOrStoreById

The useStoreOrStoreById hook is used to get a reference to a Store object from within a Provider component context, or have it passed directly to this hook.

useStoreOrStoreById(storeOrStoreId?: StoreOrStoreId): Store | undefined
TypeDescription
storeOrStoreId?StoreOrStoreId

Either an Id for accessing a Store object that was named with an Id in the Provider, or the Store object itself.

returnsStore | undefined

A reference to the Store object (or undefined if not within a Provider context, or if the requested Store object does not exist).

This is mostly of use when you are developing a component that needs a Store object and which might have been passed in explicitly to the component or is to be picked up from the context by Id (a common pattern for Store-based components).

This is unlikely to be used often. For most situations, you will want to use the useStore hook.

Example

This example creates a Provider context into which a default Store object is provided. A component within it then uses the useStoreOrStoreById hook to get a reference to the Store object again, without the need to have it passed as a prop. Note however, that unlike the useStore hook example, this component would also work if you were to pass the Store object directly into it, making it more portable.

import {Provider, useStoreOrStoreById} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = ({store}) => (
  <span>{JSON.stringify(useStoreOrStoreById(store).getTableIds())}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["pets"]</span>'
Since

v4.1.0

useDelTablesCallback

The useDelTablesCallback hook returns a callback that can be used to remove all of the tabular data in a Store.

useDelTablesCallback(
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store) => void,
  thenDeps?: DependencyList,
): Callback
TypeDescription
storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store) => void

A function which is called after the deletion, with a reference to the Store.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsCallback

A callback for subsequent use.

This hook is useful, for example, when creating an event handler that will delete data in the Store.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the deletion to your application's undo stack.

The Store to which the callback will make the deletion (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useDelTablesCallback hook to create an event handler which deletes from the Store when the span element is clicked.

import {useDelTablesCallback, useTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const App = () => {
  const handleClick = useDelTablesCallback(store, () =>
    console.log('Deleted'),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useTables(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"pets":{"nemo":{"species":"fish"}}}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Deleted'

console.log(span.innerHTML);
// -> '{}'
Since

v1.0.0

useHasTables

The useHasTables hook returns a boolean indicating whether any Table objects exist in the Store, and registers a listener so that any changes to that result will cause a re-render.

useHasTables(storeOrStoreId?: StoreOrStoreId): boolean
TypeDescription
storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsboolean

Whether any Tables exist.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useHasTables hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Tables will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useHasTables hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useHasTables} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => <span>{JSON.stringify(useHasTables(store))}</span>;

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>true</span>'

store.delTable('pets');
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useHasTables hook.

import {Provider, useHasTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useHasTables())}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>true</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useHasTables hook.

import {Provider, useHasTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useHasTables('petStore'))}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>true</span>'
Since

v4.4.0

useHasTablesListener

The useHasTablesListener hook registers a listener function with the Store that will be called when Tables as a whole are added to or removed from the Store.

useHasTablesListener(
  listener: HasTablesListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerHasTablesListener

The function that will be called whenever Tables as a whole are added or removed.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useHasTables hook).

Unlike the addHasTablesListener method, which returns a listener Id and requires you to remove it manually, the useHasTablesListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useHasTablesListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useHasTablesListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useHasTablesListener(() => console.log('Tables existence changed'));
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().hasTables);
// -> 1

store.setCell('pets', 'fido', 'color', 'brown');
// -> 'Tables existence changed'

root.unmount();
console.log(store.getListenerStats().hasTables);
// -> 0
Since

v4.4.0

useSetTablesCallback

The useSetTablesCallback hook returns a parameterized callback that can be used to set the tabular data of a Store.

useSetTablesCallback<Parameter>(
  getTables: (parameter: Parameter, store: Store) => Tables,
  getTablesDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store, tables: Tables) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
getTables(parameter: Parameter, store: Store) => Tables

A function which returns the Tables object that will be used to update the Store, based on the parameter the callback will receive (and which is most likely a DOM event).

getTablesDeps?DependencyList

An optional array of dependencies for the getTables function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store, tables: Tables) => void

A function which is called after the mutation, with a reference to the Store and the Tables used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The first parameter is a function which will produce the Tables object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useSetTablesCallback hook to create an event handler which updates the Store when the span element is clicked.

import {useSetTablesCallback, useTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const App = () => {
  const handleClick = useSetTablesCallback(
    (e) => ({pets: {nemo: {species: 'fish', bubbles: e.bubbles}}}),
    [],
    store,
    (store, tables) => console.log(`Updated: ${JSON.stringify(tables)}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useTables(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"pets":{"nemo":{"species":"fish"}}}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated: {"pets":{"nemo":{"species":"fish","bubbles":true}}}'

console.log(span.innerHTML);
// -> '{"pets":{"nemo":{"species":"fish","bubbles":true}}}'
Since

v1.0.0

useTables

The useTables hook returns a Tables object containing the tabular data of a Store, and registers a listener so that any changes to that result will cause a re-render.

useTables(storeOrStoreId?: StoreOrStoreId): Tables
TypeDescription
storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsTables

A Tables object containing the tabular data of the Store.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useTables hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Tables will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useTables hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useTables} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => <span>{JSON.stringify(useTables(store))}</span>;

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>{"pets":{"fido":{"color":"brown"}}}</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>{"pets":{"fido":{"color":"walnut"}}}</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useTables hook.

import {Provider, useTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useTables())}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>{"pets":{"fido":{"color":"brown"}}}</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useTables hook.

import {Provider, useTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useTables('petStore'))}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>{"pets":{"fido":{"color":"brown"}}}</span>'
Since

v1.0.0

useTablesListener

The useTablesListener hook registers a listener function with a Store that will be called whenever tabular data in it changes.

useTablesListener(
  listener: TablesListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerTablesListener

The function that will be called whenever tabular data in the Store changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useTables hook).

Unlike the addTablesListener method, which returns a listener Id and requires you to remove it manually, the useTablesListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useTablesListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useTablesListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useTablesListener(() => console.log('Tables changed'));
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().tables);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Tables changed'

root.unmount();
console.log(store.getListenerStats().tables);
// -> 0
Since

v1.0.0

useTableIds

The useTableIds hook returns the Ids of every Table in a Store, and registers a listener so that any changes to that result will cause a re-render.

useTableIds(storeOrStoreId?: StoreOrStoreId): Ids
TypeDescription
storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsIds

An array of the Ids of every Table in the Store.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useTableIds hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Table Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useTableIds hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useTableIds} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => <span>{JSON.stringify(useTableIds(store))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["pets"]</span>'

store.setCell('species', 'dog', 'price', 5);
console.log(app.innerHTML);
// -> '<span>["pets","species"]</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useTableIds hook.

import {Provider, useTableIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useTableIds())}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["pets"]</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useTableIds hook.

import {Provider, useTableIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useTableIds('petStore'))}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["pets"]</span>'
Since

v1.0.0

useTableIdsListener

The useTableIdsListener hook registers a listener function with a Store that will be called whenever the Table Ids in it change.

useTableIdsListener(
  listener: TableIdsListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerTableIdsListener

The function that will be called whenever the Table Ids in the Store change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useTableIds hook).

Unlike the addTableIdsListener method, which returns a listener Id and requires you to remove it manually, the useTableIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useTableIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useTableIdsListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useTableIdsListener(() => console.log('Table Ids changed'));
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().tableIds);
// -> 1

store.setTable('species', {dog: {price: 5}});
// -> 'Table Ids changed'

root.unmount();
console.log(store.getListenerStats().tableIds);
// -> 0
Since

v1.0.0

useDelTableCallback

The useDelTableCallback hook returns a parameterized callback that can be used to remove a single Table from a Store.

useDelTableCallback<Parameter>(
  tableId: string | GetId<Parameter>,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
tableIdstring | GetId<Parameter>

The Id of the Table in the Store to delete, or a GetId function that will return it.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store) => void

A function which is called after the deletion, with a reference to the Store.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of an GetId function if used as the tableId argument.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will delete data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the deletion.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the deletion to your application's undo stack.

The Store to which the callback will make the deletion (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useDelTableCallback hook to create an event handler which deletes from the Store when the span element is clicked.

import {useDelTableCallback, useTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const App = () => {
  const handleClick = useDelTableCallback('pets', store, () =>
    console.log('Deleted'),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useTables(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"pets":{"nemo":{"species":"fish"}}}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Deleted'

console.log(span.innerHTML);
// -> '{}'
Since

v1.0.0

useHasTable

The useHasTable hook returns a boolean indicating whether a given Table exists in the Store, and registers a listener so that any changes to that result will cause a re-render.

useHasTable(
  tableId: string,
  storeOrStoreId?: StoreOrStoreId,
): boolean
TypeDescription
tableIdstring

The Id of the Table in the Store.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsboolean

Whether a Table with that Id exists.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useHasTable hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Table will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useHasTable hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useHasTable} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => (
  <span>{JSON.stringify(useHasTable('pets', store))}</span>
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>true</span>'

store.delTable('pets');
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useHasTable hook.

import {Provider, useHasTable} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useHasTable('pets'))}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>true</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useHasTable hook.

import {Provider, useHasTable} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useHasTable('pets', 'petStore'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>true</span>'
Since

v4.4.0

useHasTableCell

The useHasTableCell hook returns a boolean indicating whether a given Cell exists anywhere in a Table, not just in a specific Row, and registers a listener so that any changes to that result will cause a re-render.

useHasTableCell(
  tableId: string,
  cellId: string,
  storeOrStoreId?: StoreOrStoreId,
): boolean
TypeDescription
tableIdstring

The Id of the Table in the Store.

cellIdstring

The Id of the Cell in the Table.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsboolean

Whether a Cell with that Id exists anywhere in that Table.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useHasTableCell hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Table will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useHasTableCell hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useHasTableCell} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => (
  <span>{JSON.stringify(useHasTableCell('pets', 'legs', store))}</span>
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>false</span>'

store.setRow('pets', 'felix', {color: 'black', legs: 4});
console.log(app.innerHTML);
// -> '<span>true</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useHasTableCell hook.

import {Provider, useHasTableCell} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useHasTableCell('pets', 'legs'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useHasTableCell hook.

import {Provider, useHasTableCell} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(useHasTableCell('pets', 'legs', 'petStore'))}
  </span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>false</span>'
Since

v4.4.0

useHasTableCellListener

The useHasTableCellListener hook registers a listener function with the Store that will be called when a Cell is added to or removed from anywhere in a Table as a whole.

useHasTableCellListener(
  tableId: IdOrNull,
  cellId: IdOrNull,
  listener: HasTableCellListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerHasTableCellListener

The function that will be called whenever the matching Cell is added to or removed from anywhere in the Table.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useHasTableCell hook).

You can either listen to a single Table Cell being added or removed (by specifying the Table Id and Cell Id, as the method's first two parameters) or changes to any Table Cell (by providing null wildcards).

Unlike the addHasTableCellIds method, which returns a listener Id and requires you to remove it manually, the useHasTableCellListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useHasTableCellListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useHasTableCellListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useHasTableCellListener('pets', 'color', () =>
    console.log('Table Cell existence changed'),
  );
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().hasTableCell);
// -> 1

store.setRow('pets', 'fido', {color: 'brown'});
// -> 'Table Cell existence changed'

root.unmount();
console.log(store.getListenerStats().hasTableCell);
// -> 0
Since

v4.4.0

useHasTableListener

The useHasTableListener hook registers a listener function with the Store that will be called when a Table is added to or removed from the Store.

useHasTableListener(
  tableId: IdOrNull,
  listener: HasTableListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerHasTableListener

The function that will be called whenever the matching Table is added or removed.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useHasTable hook).

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Unlike the addHasTableListener method, which returns a listener Id and requires you to remove it manually, the useHasTableListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useHasTableListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useHasTableListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useHasTableListener('pets', () =>
    console.log('Table existence changed'),
  );
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().hasTable);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Table existence changed'

root.unmount();
console.log(store.getListenerStats().hasTable);
// -> 0
Since

v4.4.0

useSetTableCallback

The useSetTableCallback hook returns a parameterized callback that can be used to set the data of a single Table in a Store.

useSetTableCallback<Parameter>(
  tableId: string | GetId<Parameter>,
  getTable: (parameter: Parameter, store: Store) => Table,
  getTableDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store, table: Table) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
tableIdstring | GetId<Parameter>

The Id of the Table in the Store to set, or a GetId function that will return it.

getTable(parameter: Parameter, store: Store) => Table

A function which returns the Table object that will be used to update the Store, based on the parameter the callback will receive (and which is most likely a DOM event).

getTableDeps?DependencyList

An optional array of dependencies for the getTable function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of a GetId function if used as the tableId argument.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store, table: Table) => void

A function which is called after the mutation, with a reference to the Store and the Table used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The second parameter is a function which will produce the Table object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional third parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useSetTableCallback hook to create an event handler which updates the Store when the span element is clicked.

import {useSetTableCallback, useTable} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTable('pets', {nemo: {species: 'fish'}});
const App = () => {
  const handleClick = useSetTableCallback(
    'pets',
    (e) => ({nemo: {species: 'fish', bubbles: e.bubbles}}),
    [],
    store,
    (store, table) => console.log(`Updated: ${JSON.stringify(table)}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useTable('pets', store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"nemo":{"species":"fish"}}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated: {"nemo":{"species":"fish","bubbles":true}}'

console.log(span.innerHTML);
// -> '{"nemo":{"species":"fish","bubbles":true}}'
Since

v1.0.0

useTable

The useTable hook returns an object containing the data of a single Table in a Store, and registers a listener so that any changes to that result will cause a re-render.

useTable(
  tableId: string,
  storeOrStoreId?: StoreOrStoreId,
): Table
TypeDescription
tableIdstring

The Id of the Table in the Store.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsTable

An object containing the entire data of the Table.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useTable hook lets you indicate which Store to get data for: omit the final optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Table will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useTable hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useTable} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => <span>{JSON.stringify(useTable('pets', store))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>{"fido":{"color":"brown"}}</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>{"fido":{"color":"walnut"}}</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useTable hook.

import {Provider, useTable} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useTable('pets'))}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>{"fido":{"color":"brown"}}</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useTable hook.

import {Provider, useTable} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useTable('pets', 'petStore'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>{"fido":{"color":"brown"}}</span>'
Since

v1.0.0

useTableCellIds

The useTableCellIds hook returns the Ids of every Cell used across the whole Table, and registers a listener so that any changes to that result will cause a re-render.

useTableCellIds(
  tableId: string,
  storeOrStoreId?: StoreOrStoreId,
): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsIds

An array of the Ids of every Cell used across the whole Table.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useTableCellIds hook lets you indicate which Store to get data for: omit the optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Table Cell Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useTableCellIds hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useTableCellIds} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => (
  <span>{JSON.stringify(useTableCellIds('pets', store))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["color"]</span>'

store.setCell('pets', 'felix', 'species', 'cat');
console.log(app.innerHTML);
// -> '<span>["color","species"]</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useTableCellIds hook.

import {Provider, useTableCellIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useTableCellIds('pets'))}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["color"]</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useTableCellIds hook.

import {Provider, useTableCellIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useTableCellIds('pets', 'petStore'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["color"]</span>'
Since

v3.3.0

useTableCellIdsListener

The useTableCellIdsListener hook registers a listener function with a Store that will be called whenever the Cell Ids that appear anywhere in a Table change.

useTableCellIdsListener(
  tableId: IdOrNull,
  listener: TableCellIdsListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerTableCellIdsListener

The function that will be called whenever the Cell Ids that appear anywhere in a Table change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useTableCellIds hook).

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing null).

Unlike the addTableCellIdsListener method, which returns a listener Id and requires you to remove it manually, the useTableCellIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useTableCellIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useTableCellIdsListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useTableCellIdsListener('pets', () => console.log('Cell Ids changed'));
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().tableCellIds);
// -> 1

store.setRow('pets', 'felix', {species: 'cat'});
// -> 'Cell Ids changed'

root.unmount();
console.log(store.getListenerStats().rowIds);
// -> 0
Since

v1.0.0

useTableListener

The useTableListener hook registers a listener function with a Store that will be called whenever data in a Table changes.

useTableListener(
  tableId: IdOrNull,
  listener: TableListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerTableListener

The function that will be called whenever data in the Table changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useTable hook).

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing a null wildcard).

Unlike the addTableListener method, which returns a listener Id and requires you to remove it manually, the useTableListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useTableListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useTableListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useTableListener('pets', () => console.log('Table changed'));
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().table);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Table changed'

root.unmount();
console.log(store.getListenerStats().table);
// -> 0
Since

v1.0.0

useRowIds

The useRowIds hook returns the Ids of every Row in a given Table, and registers a listener so that any changes to that result will cause a re-render.

useRowIds(
  tableId: string,
  storeOrStoreId?: StoreOrStoreId,
): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsIds

An array of the Ids of every Row in the Table.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useRowIds hook lets you indicate which Store to get data for: omit the optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Row Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useRowIds hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useRowIds} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => <span>{JSON.stringify(useRowIds('pets', store))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["fido"]</span>'

store.setCell('pets', 'felix', 'color', 'black');
console.log(app.innerHTML);
// -> '<span>["fido","felix"]</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useRowIds hook.

import {Provider, useRowIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useRowIds('pets'))}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["fido"]</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useRowIds hook.

import {Provider, useRowIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useRowIds('pets', 'petStore'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["fido"]</span>'
Since

v1.0.0

useRowIdsListener

The useRowIdsListener hook registers a listener function with a Store that will be called whenever the Row Ids in a Table change.

useRowIdsListener(
  tableId: IdOrNull,
  listener: RowIdsListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerRowIdsListener

The function that will be called whenever the Row Ids in the Table change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useRowIds hook).

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing null).

Unlike the addRowIdsListener method, which returns a listener Id and requires you to remove it manually, the useRowIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useRowIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useRowIdsListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useRowIdsListener('pets', () => console.log('Row Ids changed'));
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().rowIds);
// -> 1

store.setRow('pets', 'felix', {color: 'black'});
// -> 'Row Ids changed'

root.unmount();
console.log(store.getListenerStats().rowIds);
// -> 0
Since

v1.0.0

useSortedRowIds

The useSortedRowIds hook returns the sorted (and optionally, paginated) Ids of every Row in a given Table, and registers a listener so that any changes to that result will cause a re-render.

useSortedRowIds(
  tableId: string,
  cellId?: string,
  descending?: boolean,
  offset?: number,
  limit?: number,
  storeOrStoreId?: StoreOrStoreId,
): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

cellId?string

The Id of the Cell whose values are used for the sorting, or undefined to by sort the Row Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of Row Ids to skip for pagination purposes, if any.

limit?number

The maximum number of Row Ids to return, or undefined for all.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsIds

An array of the sorted Ids of every Row in the Table.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useSortedRowIds hook lets you indicate which Store to get data for: omit the optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the sorted Row Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useSortedRowIds hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useSortedRowIds} from 'tinybase/ui-react';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
const App = () => (
  <span>
    {JSON.stringify(
      useSortedRowIds('pets', 'species', false, 0, undefined, store),
    )}
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["felix","fido"]</span>'

store.setRow('pets', 'cujo', {species: 'wolf'});
console.log(app.innerHTML);
// -> '<span>["felix","fido","cujo"]</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useSortedRowIds hook.

import {Provider, useSortedRowIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useSortedRowIds('pets'))}</span>;

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["felix","fido"]</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useSortedRowIds hook.

import {Provider, useSortedRowIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(
      useSortedRowIds('pets', 'species', false, 0, undefined, 'petStore'),
    )}
  </span>
);

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["felix","fido"]</span>'
Since

v2.0.0

useSortedRowIdsListener

The useSortedRowIdsListener hook registers a listener function with a Store that will be called whenever sorted (and optionally, paginated) Row Ids in a Table change.

useSortedRowIdsListener(
  tableId: string,
  cellId: undefined | string,
  descending: boolean,
  offset: number,
  limit: undefined | number,
  listener: SortedRowIdsListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdstring

The Id of the Table in the Store.

cellIdundefined | string

The Id of the Cell whose values are used for the sorting, or undefined to by sort the Row Id itself.

descendingboolean

Whether the sorting should be in descending order.

offsetnumber

The number of Row Ids to skip for pagination purposes, if any.

limitundefined | number

The maximum number of Row Ids to return, or undefined for all.

listenerSortedRowIdsListener

The function that will be called whenever the sorted Row Ids in the Table change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useSortedRowIds hook).

Unlike the addSortedRowIdsListener method, which returns a listener Id and requires you to remove it manually, the useSortedRowIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useSortedRowIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useSortedRowIdsListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useSortedRowIdsListener('pets', 'species', false, 0, undefined, () =>
    console.log('Sorted Row Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().sortedRowIds);
// -> 1

store.setRow('pets', 'cujo', {species: 'wolf'});
// -> 'Sorted Row Ids changed'

root.unmount();
console.log(store.getListenerStats().sortedRowIds);
// -> 0
Since

v2.0.0

useAddRowCallback

The useAddRowCallback hook returns a parameterized callback that can be used to create a new Row in a Store.

useAddRowCallback<Parameter>(
  tableId: string | GetId<Parameter>,
  getRow: (parameter: Parameter, store: Store) => Row,
  getRowDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (rowId: undefined | string, store: Store, row: Row) => void,
  thenDeps?: DependencyList,
  reuseRowIds?: boolean,
): ParameterizedCallback<Parameter>
TypeDescription
tableIdstring | GetId<Parameter>

The Id of the Table in the Store, or a GetId function that will return it.

getRow(parameter: Parameter, store: Store) => Row

A function which returns the Row object that will be used to update the Store, based on the parameter the callback will receive (and which is most likely a DOM event).

getRowDeps?DependencyList

An optional array of dependencies for the getRow function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of a GetId function if used as the tableId argument.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(rowId: undefined | string, store: Store, row: Row) => void

A function which is called after the mutation, with the new Row Id, a reference to the Store, and the Row used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

reuseRowIds?boolean

Whether Ids should be recycled from previously deleted Row objects, defaulting to true.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The second parameter is a function which will produce the Row object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional third parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

The reuseRowIds parameter defaults to true, which means that if you delete a Row and then add another, the Id will be re-used - unless you delete the entire Table, in which case all Row Ids will reset. Otherwise, if you specify reuseRowIds to be false, then the Id will be a monotonically increasing string representation of an increasing integer, regardless of any you may have previously deleted.

Example

This example uses the useAddRowCallback hook to create an event handler which updates the Store when the span element is clicked.

import {useAddRowCallback, useTable} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setRow('pets', 'nemo', {species: 'fish'});
const App = () => {
  const handleClick = useAddRowCallback(
    'pets',
    (e) => ({species: 'frog', bubbles: e.bubbles}),
    [],
    store,
    (rowId) => console.log(`Added row: ${rowId}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useTable('pets', store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"nemo":{"species":"fish"}}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Added row: 0'

console.log(span.innerHTML);
// -> '{"0":{"species":"frog","bubbles":true},"nemo":{"species":"fish"}}'
Since

v1.0.0

useDelRowCallback

The useDelRowCallback hook returns a parameterized callback that can be used to remove a single Row from a Table.

useDelRowCallback<Parameter>(
  tableId: string | GetId<Parameter>,
  rowId: string | GetId<Parameter>,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
tableIdstring | GetId<Parameter>

The Id of the Table in the Store, or a GetId function that will return it.

rowIdstring | GetId<Parameter>

The Id of the Row in the Table to delete, or a GetId function that will return it.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store) => void

A function which is called after the deletion, with a reference to the Store.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of any GetId functions if used as the tableId or rowId arguments.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will delete data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the deletion.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the deletion to your application's undo stack.

The Store to which the callback will make the deletion (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useDelRowCallback hook to create an event handler which deletes from the Store when the span element is clicked.

import {useDelRowCallback, useTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const App = () => {
  const handleClick = useDelRowCallback('pets', 'nemo', store, () =>
    console.log('Deleted'),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useTables(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"pets":{"nemo":{"species":"fish"}}}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Deleted'

console.log(span.innerHTML);
// -> '{}'
Since

v1.0.0

useHasRow

The useHasRow hook returns a boolean indicating whether a given Row exists in the Store, and registers a listener so that any changes to that result will cause a re-render.

useHasRow(
  tableId: string,
  rowId: string,
  storeOrStoreId?: StoreOrStoreId,
): boolean
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsboolean

Whether a Row with that Id exists in that Table.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useHasRow hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Row will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useHasRow hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useHasRow} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => (
  <span>{JSON.stringify(useHasRow('pets', 'felix', store))}</span>
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>false</span>'

store.setCell('pets', 'felix', 'color', 'black');
console.log(app.innerHTML);
// -> '<span>true</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useHasRow hook.

import {Provider, useHasRow} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useHasRow('pets', 'felix'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useHasRow hook.

import {Provider, useHasRow} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useHasRow('pets', 'felix', 'petStore'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>false</span>'
Since

v4.4.0

useHasRowListener

The useHasRowListener hook registers a listener function with the Store that will be called when a Row is added to or removed from the Store.

useHasRowListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: HasRowListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerHasRowListener

The function that will be called whenever the matching Row is added or removed.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useHasRow hook).

You can either listen to a single Row being added or removed (by specifying the Table Id and Row Id, as the method's first two parameters) or changes to any Row (by providing null wildcards).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Unlike the addHasRowListener method, which returns a listener Id and requires you to remove it manually, the useHasRowListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useHasRowListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useHasRowListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useHasRowListener('pets', 'fido', () =>
    console.log('Row existence changed'),
  );
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().hasRow);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Row existence changed'

root.unmount();
console.log(store.getListenerStats().hasRow);
// -> 0
Since

v4.4.0

useRow

The useRow hook returns an object containing the data of a single Row in a given Table, and registers a listener so that any changes to that result will cause a re-render.

useRow(
  tableId: string,
  rowId: string,
  storeOrStoreId?: StoreOrStoreId,
): Row
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsRow

An object containing the entire data of the Row.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useRow hook lets you indicate which Store to get data for: omit the final optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Row will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useRow hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useRow} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => (
  <span>{JSON.stringify(useRow('pets', 'fido', store))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>{"color":"brown"}</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>{"color":"walnut"}</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useRow hook.

import {Provider, useRow} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useRow('pets', 'fido'))}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>{"color":"brown"}</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useRow hook.

import {Provider, useRow} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useRow('pets', 'fido', 'petStore'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>{"color":"brown"}</span>'
Since

v1.0.0

useRowCount

The useRowCount hook returns the count of the Row objects in a given Table, and registers a listener so that any changes to that result will cause a re-render.

useRowCount(
  tableId: string,
  storeOrStoreId?: StoreOrStoreId,
): number
TypeDescription
tableIdstring

The Id of the Table in the Store.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsnumber

The number of Row objects in the Table.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useRowCount hook lets you indicate which Store to get data for: omit the optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the count of Row objects will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useRowCount hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useRowCount} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => <span>{useRowCount('pets', store)}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>1</span>'

store.setCell('pets', 'felix', 'color', 'black');
console.log(app.innerHTML);
// -> '<span>2</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useRowCount hook.

import {Provider, useRowCount} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useRowCount('pets')}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>1</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useRowCount hook.

import {Provider, useRowCount} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useRowCount('pets', 'petStore')}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>1</span>'
Since

v4.1.0

useRowCountListener

The useRowCountListener hook registers a listener function with a Store that will be called whenever the count of the Row objects in a Table changes.

useRowCountListener(
  tableId: IdOrNull,
  listener: RowCountListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

listenerRowCountListener

The function that will be called whenever the count of the Row objects in the Table changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useRowCount hook).

You can either listen to a single Table (by specifying its Id as the method's first parameter) or changes to any Table (by providing null).

Unlike the addRowCountListener method, which returns a listener Id and requires you to remove it manually, the useRowCountListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useRowCountListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useRowCountListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useRowCountListener('pets', () => console.log('Row count changed'));
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().rowCount);
// -> 1

store.setRow('pets', 'felix', {color: 'black'});
// -> 'Row count changed'

root.unmount();
console.log(store.getListenerStats().rowCount);
// -> 0
Since

v4.1.0

useRowListener

The useRowListener hook registers a listener function with a Store that will be called whenever data in a Row changes.

useRowListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: RowListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerRowListener

The function that will be called whenever data in the Row changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useRow hook).

You can either listen to a single Row (by specifying the Table Id and Row Id as the method's first two parameters) or changes to any Row (by providing null wildcards).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Unlike the addRowListener method, which returns a listener Id and requires you to remove it manually, the useRowListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useRowListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useRowListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useRowListener('pets', 'fido', () => console.log('Row changed'));
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().row);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Row changed'

root.unmount();
console.log(store.getListenerStats().row);
// -> 0
Since

v1.0.0

useSetPartialRowCallback

The useSetPartialRowCallback hook returns a parameterized callback that can be used to set partial data of a single Row in the Store, leaving other Cell values unaffected.

useSetPartialRowCallback<Parameter>(
  tableId: string | GetId<Parameter>,
  rowId: string | GetId<Parameter>,
  getPartialRow: (parameter: Parameter, store: Store) => Row,
  getPartialRowDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store, partialRow: Row) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
tableIdstring | GetId<Parameter>

The Id of the Table in the Store, or a GetId function that will return it.

rowIdstring | GetId<Parameter>

The Id of the Row in the Table to set, or a GetId function that will return it.

getPartialRow(parameter: Parameter, store: Store) => Row

A function which returns the partial Row object that will be used to update the Store, based on the parameter the callback will receive (and which is most likely a DOM event).

getPartialRowDeps?DependencyList

An optional array of dependencies for the getRow function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of any GetId functions if used as the tableId, rowId, or cellId arguments.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store, partialRow: Row) => void

A function which is called after the mutation, with a reference to the Store and the Row used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The third parameter is a function which will produce the partial Row object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional fourth parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useSetPartialRowCallback hook to create an event handler which updates the Store when the span element is clicked.

import {useRow, useSetPartialRowCallback} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setRow('pets', 'nemo', {species: 'fish'});
const App = () => {
  const handleClick = useSetPartialRowCallback(
    'pets',
    'nemo',
    (e) => ({bubbles: e.bubbles}),
    [],
    store,
    (store, partialRow) =>
      console.log(`Updated: ${JSON.stringify(partialRow)}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useRow('pets', 'nemo', store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"species":"fish"}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated: {"bubbles":true}'

console.log(span.innerHTML);
// -> '{"species":"fish","bubbles":true}'
Since

v1.0.0

useSetRowCallback

The useSetRowCallback hook returns a parameterized callback that can be used to set the data of a single Row in a Store.

useSetRowCallback<Parameter>(
  tableId: string | GetId<Parameter>,
  rowId: string | GetId<Parameter>,
  getRow: (parameter: Parameter, store: Store) => Row,
  getRowDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store, row: Row) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
tableIdstring | GetId<Parameter>

The Id of the Table in the Store, or a GetId function that will return it.

rowIdstring | GetId<Parameter>

The Id of the Row in the Table to set, or a GetId function that will return it.

getRow(parameter: Parameter, store: Store) => Row

A function which returns the Row object that will be used to update the Store, based on the parameter the callback will receive (and which is most likely a DOM event).

getRowDeps?DependencyList

An optional array of dependencies for the getRow function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of any GetId functions if used as the tableId or rowId arguments.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store, row: Row) => void

A function which is called after the mutation, with a reference to the Store and the Row used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The third parameter is a function which will produce the Row object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional fourth parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useSetRowCallback hook to create an event handler which updates the Store when the span element is clicked.

import {useRow, useSetRowCallback} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setRow('pets', 'nemo', {species: 'fish'});
const App = () => {
  const handleClick = useSetRowCallback(
    'pets',
    'nemo',
    (e) => ({species: 'fish', bubbles: e.bubbles}),
    [],
    store,
    (store, row) => console.log(`Updated: ${JSON.stringify(row)}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useRow('pets', 'nemo', store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"species":"fish"}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated: {"species":"fish","bubbles":true}'

console.log(span.innerHTML);
// -> '{"species":"fish","bubbles":true}'
Since

v1.0.0

useCellIds

The useCellIds hook returns the Ids of every Cell in a given Row, in a given Table, and registers a listener so that any changes to that result will cause a re-render.

useCellIds(
  tableId: string,
  rowId: string,
  storeOrStoreId?: StoreOrStoreId,
): Ids
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsIds

An array of the Ids of every Cell in the Row.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useCellIds hook lets you indicate which Store to get data for: omit the optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Cell Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useCellIds hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useCellIds} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => (
  <span>{JSON.stringify(useCellIds('pets', 'fido', store))}</span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["color"]</span>'

store.setCell('pets', 'fido', 'species', 'dog');
console.log(app.innerHTML);
// -> '<span>["color","species"]</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useCellIds hook.

import {Provider, useCellIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useCellIds('pets', 'fido'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["color"]</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useCellIds hook.

import {Provider, useCellIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useCellIds('pets', 'fido', 'petStore'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["color"]</span>'
Since

v1.0.0

useCellIdsListener

The useCellIdsListener hook registers a listener function with a Store that will be called whenever the Cell Ids in a Row change.

useCellIdsListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  listener: CellIdsListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

listenerCellIdsListener

The function that will be called whenever the Cell Ids in the Row change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useCellIds hook).

You can either listen to a single Row (by specifying the Table Id and Row Id as the method's first two parameters) or changes to any Row (by providing null wildcards).

Both, either, or neither of the tableId and rowId parameters can be wildcarded with null. You can listen to a specific Row in a specific Table, any Row in a specific Table, a specific Row in any Table, or any Row in any Table.

Unlike the addCellIdsListener method, which returns a listener Id and requires you to remove it manually, the useCellIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useCellIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useCellIdsListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useCellIdsListener('pets', 'fido', () =>
    console.log('Cell Ids changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().cellIds);
// -> 1

store.setCell('pets', 'fido', 'species', 'dog');
// -> 'Cell Ids changed'

root.unmount();
console.log(store.getListenerStats().cellIds);
// -> 0
Since

v1.0.0

useCell

The useCell hook returns an object containing the value of a single Cell in a given Row, in a given Table, and registers a listener so that any changes to that result will cause a re-render.

useCell(
  tableId: string,
  rowId: string,
  cellId: string,
  storeOrStoreId?: StoreOrStoreId,
): CellOrUndefined
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

cellIdstring

The Id of the Cell in the Row.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsCellOrUndefined

The value of the Cell.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useCell hook lets you indicate which Store to get data for: omit the final optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Cell will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useCell hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useCell} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => <span>{useCell('pets', 'fido', 'color', store)}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>brown</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>walnut</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useCell hook.

import {Provider, useCell} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useCell('pets', 'fido', 'color')}</span>;

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>brown</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useCell hook.

import {Provider, useCell} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useCell('pets', 'fido', 'color', 'petStore')}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>brown</span>'
Since

v1.0.0

useCellListener

The useCellListener hook registers a listener function with a Store that will be called whenever data in a Cell changes.

useCellListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: CellListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerCellListener

The function that will be called whenever data in the Cell changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useCell hook).

You can either listen to a single Cell (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or changes to any Cell (by providing null wildcards).

All, some, or none of the tableId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific Row in a specific Table, any Cell in any Row in any Table, for example - or every other combination of wildcards.

Unlike the addCellListener method, which returns a listener Id and requires you to remove it manually, the useCellListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useCellListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useCellListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useCellListener('pets', 'fido', 'color', () =>
    console.log('Cell changed'),
  );
  return <span>App</span>;
};

const store = createStore().setTables({pets: {fido: {color: 'brown'}}});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().cell);
// -> 1

store.setCell('pets', 'fido', 'color', 'walnut');
// -> 'Cell changed'

root.unmount();
console.log(store.getListenerStats().cell);
// -> 0
Since

v1.0.0

useDelCellCallback

The useDelCellCallback hook returns a parameterized callback that can be used to remove a single Cell from a Row.

useDelCellCallback<Parameter>(
  tableId: string | GetId<Parameter>,
  rowId: string | GetId<Parameter>,
  cellId: string | GetId<Parameter>,
  forceDel?: boolean,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
tableIdstring | GetId<Parameter>

The Id of the Table in the Store, or a GetId function that will return it.

rowIdstring | GetId<Parameter>

The Id of the Row in the Table, or a GetId function that will return it.

cellIdstring | GetId<Parameter>

The Id of the Cell in the Row to delete, or a GetId function that will return it.

forceDel?boolean

An optional flag to indicate that the whole Row should be deleted, even if a TablesSchema provides a default value for this Cell. Defaults to false.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store) => void

A function which is called after the deletion, with a reference to the Store.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of any GetId functions if used as the tableId, rowId, or cellId arguments.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will delete data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the deletion.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the deletion to your application's undo stack.

The Store to which the callback will make the deletion (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useDelCellCallback hook to create an event handler which deletes from the Store when the span element is clicked.

import {useDelCellCallback, useTables} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {nemo: {species: 'fish'}}});
const App = () => {
  const handleClick = useDelCellCallback(
    'pets',
    'nemo',
    'species',
    false,
    store,
    () => console.log('Deleted'),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useTables(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"pets":{"nemo":{"species":"fish"}}}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Deleted'

console.log(span.innerHTML);
// -> '{}'
Since

v1.0.0

useHasCell

The useHasCell hook returns a boolean indicating whether a given Cell exists in a given Row in a given Table, and registers a listener so that any changes to that result will cause a re-render.

useHasCell(
  tableId: string,
  rowId: string,
  cellId: string,
  storeOrStoreId?: StoreOrStoreId,
): boolean
TypeDescription
tableIdstring

The Id of the Table in the Store.

rowIdstring

The Id of the Row in the Table.

cellIdstring

The Id of the Cell in the Row.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsboolean

Whether a Cell with that Id exists in that Row in that Table.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useHasCell hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Cell will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useHasCell hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useHasCell} from 'tinybase/ui-react';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => (
  <span>{JSON.stringify(useHasCell('pets', 'fido', 'legs', store))}</span>
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>false</span>'

store.setCell('pets', 'fido', 'legs', 4);
console.log(app.innerHTML);
// -> '<span>true</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useHasCell hook.

import {Provider, useHasCell} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useHasCell('pets', 'fido', 'legs'))}</span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useHasCell hook.

import {Provider, useHasCell} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    {JSON.stringify(useHasCell('pets', 'fido', 'legs', 'petStore'))}
  </span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>false</span>'
Since

v4.4.0

useHasCellListener

The useHasCellListener hook registers a listener function with the Store that will be called when a Cell is added to or removed from the Store.

useHasCellListener(
  tableId: IdOrNull,
  rowId: IdOrNull,
  cellId: IdOrNull,
  listener: HasCellListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
tableIdIdOrNull

The Id of the Table to listen to, or null as a wildcard.

rowIdIdOrNull

The Id of the Row to listen to, or null as a wildcard.

cellIdIdOrNull

The Id of the Cell to listen to, or null as a wildcard.

listenerHasCellListener

The function that will be called whenever the matching Cell is added or removed.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useHasCell hook).

You can either listen to a single Cell being added or removed (by specifying the Table Id, Row Id, and Cell Id as the method's first three parameters) or changes to any Cell (by providing null wildcards).

All, some, or none of the tableId, rowId, and cellId parameters can be wildcarded with null. You can listen to a specific Cell in a specific Row in a specific Table, any Cell in any Row in any Table, for example - or every other combination of wildcards.

Unlike the addHasCellListener method, which returns a listener Id and requires you to remove it manually, the useHasCellListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useHasCellListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useHasCellListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useHasCellListener('pets', 'fido', 'color', () =>
    console.log('Cell existence changed'),
  );
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().hasCell);
// -> 1

store.setCell('pets', 'fido', 'color', 'brown');
// -> 'Cell existence changed'

root.unmount();
console.log(store.getListenerStats().hasCell);
// -> 0
Since

v4.4.0

useSetCellCallback

The useSetCellCallback hook returns a parameterized callback that can be used to set the value of a single Cell in a Store.

useSetCellCallback<Parameter>(
  tableId: string | GetId<Parameter>,
  rowId: string | GetId<Parameter>,
  cellId: string | GetId<Parameter>,
  getCell: (parameter: Parameter, store: Store) => Cell | MapCell,
  getCellDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store, cell: Cell | MapCell) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
tableIdstring | GetId<Parameter>

The Id of the Table in the Store, or a GetId function that will return it.

rowIdstring | GetId<Parameter>

The Id of the Row in the Table, or a GetId function that will return it.

cellIdstring | GetId<Parameter>

The Id of the Cell in the Row to set, or a GetId function that will return it.

getCell(parameter: Parameter, store: Store) => Cell | MapCell

A function which returns the Cell value that will be used to update the Store, or a MapCell function to update it, based on the parameter the callback will receive (and which is most likely a DOM event).

getCellDeps?DependencyList

An optional array of dependencies for the getCell function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of any GetId functions if used as the tableId, rowId, or cellId arguments.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store, cell: Cell | MapCell) => void

A function which is called after the mutation, with a reference to the Store and the Cell value (or MapCell function) used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The fourth parameter is a function which will produce the Cell object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional fourth parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Examples

This example uses the useSetCellCallback hook to create an event handler which updates the Store with a Cell value when the span element is clicked.

import {useRow, useSetCellCallback} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setCell('pets', 'nemo', 'species', 'fish');
const App = () => {
  const handleClick = useSetCellCallback(
    'pets',
    'nemo',
    'bubbles',
    (e) => e.bubbles,
    [],
    store,
    (store, cell) => console.log(`Updated: ${cell}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useRow('pets', 'nemo', store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"species":"fish"}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated: true'

console.log(span.innerHTML);
// -> '{"species":"fish","bubbles":true}'

This example uses the useSetCellCallback hook to create an event handler which updates the Store via a MapCell function when the span element is clicked.

import {useRow, useSetCellCallback} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setCell('pets', 'nemo', 'visits', 1);
const App = () => {
  const handleClick = useSetCellCallback(
    'pets',
    'nemo',
    'visits',
    (e) => (visits) => visits + (e.bubbles ? 1 : 0),
    [],
    store,
    () => console.log(`Updated with MapCell function`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useRow('pets', 'nemo', store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"visits":1}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated with MapCell function'

console.log(span.innerHTML);
// -> '{"visits":2}'
Since

v1.0.0

useDelValuesCallback

The useDelValuesCallback hook returns a callback that can be used to remove all of the keyed value data in a Store.

useDelValuesCallback(
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store) => void,
  thenDeps?: DependencyList,
): Callback
TypeDescription
storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store) => void

A function which is called after the deletion, with a reference to the Store.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsCallback

A callback for subsequent use.

This hook is useful, for example, when creating an event handler that will delete data in a Store.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the deletion to your application's undo stack.

The Store to which the callback will make the deletion (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useDelValuesCallback hook to create an event handler which deletes from the Store when the span element is clicked.

import {useDelValuesCallback, useValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const App = () => {
  const handleClick = useDelValuesCallback(store, () =>
    console.log('Deleted'),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useValues(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"open":true}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Deleted'

console.log(span.innerHTML);
// -> '{}'
Since

v3.0.0

useHasValues

The useHasValues hook returns a boolean indicating whether any Values exist in the Store, and registers a listener so that any changes to that result will cause a re-render.

useHasValues(storeOrStoreId?: StoreOrStoreId): boolean
TypeDescription
storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsboolean

Whether any Values exist.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useHasValues hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Values will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useHasValues hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useHasValues} from 'tinybase/ui-react';

const store = createStore().setValue('open', true);
const App = () => <span>{JSON.stringify(useHasValues(store))}</span>;

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>true</span>'

store.delValue('open');
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useHasValues hook.

import {Provider, useHasValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useHasValues())}</span>;

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>true</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useHasValues hook.

import {Provider, useHasValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useHasValues('petStore'))}</span>;

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>true</span>'
Since

v4.4.0

useHasValuesListener

The useHasValuesListener hook registers a listener function with the Store that will be called when Values as a whole are added to or removed from the Store.

useHasValuesListener(
  listener: HasValuesListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerHasValuesListener

The function that will be called whenever Values as a whole are added or removed.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useHasValues hook).

Unlike the addHasValuesListener method, which returns a listener Id and requires you to remove it manually, the useHasValuesListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useHasValuesListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useHasValuesListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useHasValuesListener(() => console.log('Values existence changed'));
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().hasValues);
// -> 1

store.setValue('open', true);
// -> 'Values existence changed'

root.unmount();
console.log(store.getListenerStats().hasValues);
// -> 0
Since

v4.4.0

useSetPartialValuesCallback

The useSetPartialValuesCallback hook returns a parameterized callback that can be used to set partial Values data in the Store, leaving other Values unaffected.

useSetPartialValuesCallback<Parameter>(
  getPartialValues: (parameter: Parameter, store: Store) => Values,
  getPartialValuesDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store, partialValues: Values) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
getPartialValues(parameter: Parameter, store: Store) => Values

A function which returns the partial Values object that will be used to update the Store, based on the parameter the callback will receive (and which is most likely a DOM event).

getPartialValuesDeps?DependencyList

An optional array of dependencies for the getValues function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store, partialValues: Values) => void

A function which is called after the mutation, with a reference to the Store and the Values used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The third parameter is a function which will produce the partial Values object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional fourth parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useSetPartialValuesCallback hook to create an event handler which updates the Store when the span element is clicked.

import {useSetPartialValuesCallback, useValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const App = () => {
  const handleClick = useSetPartialValuesCallback(
    (e) => ({bubbles: e.bubbles}),
    [],
    store,
    (store, partialValues) =>
      console.log(`Updated: ${JSON.stringify(partialValues)}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useValues(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"open":true}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated: {"bubbles":true}'

console.log(span.innerHTML);
// -> '{"open":true,"bubbles":true}'
Since

v3.0.0

useSetValuesCallback

The useSetValuesCallback hook returns a parameterized callback that can be used to set the keyed value data of a Store.

useSetValuesCallback<Parameter>(
  getValues: (parameter: Parameter, store: Store) => Values,
  getValuesDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store, values: Values) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
getValues(parameter: Parameter, store: Store) => Values

A function which returns the Values object that will be used to update the Store, based on the parameter the callback will receive (and which is most likely a DOM event).

getValuesDeps?DependencyList

An optional array of dependencies for the getValues function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store, values: Values) => void

A function which is called after the mutation, with a reference to the Store and the Values used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The first parameter is a function which will produce the Values object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional second parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useSetValuesCallback hook to create an event handler which updates the Store when the span element is clicked.

import {useSetValuesCallback, useValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setValues({open: true});
const App = () => {
  const handleClick = useSetValuesCallback(
    (e) => ({bubbles: e.bubbles}),
    [],
    store,
    (store, values) => console.log(`Updated: ${JSON.stringify(values)}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useValues(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"open":true}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated: {"bubbles":true}'

console.log(span.innerHTML);
// -> '{"bubbles":true}'
Since

v3.0.0

useValues

The useValues hook returns a Values object containing the keyed value data of a Store, and registers a listener so that any changes to that result will cause a re-render.

useValues(storeOrStoreId?: StoreOrStoreId): Values
TypeDescription
storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsValues

A Values object containing the keyed value data of the Store.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useValues hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Values will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useValues hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useValues} from 'tinybase/ui-react';

const store = createStore().setValue('open', true);
const App = () => <span>{JSON.stringify(useValues(store))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>{"open":true}</span>'

store.setValue('open', false);
console.log(app.innerHTML);
// -> '<span>{"open":false}</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useValues hook.

import {Provider, useValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useValues())}</span>;

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>{"open":true}</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useValues hook.

import {Provider, useValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useValues('petStore'))}</span>;

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>{"open":true}</span>'
Since

v3.0.0

useValuesListener

The useValuesListener hook registers a listener function with a Store that will be called whenever keyed value data in it changes.

useValuesListener(
  listener: ValuesListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerValuesListener

The function that will be called whenever keyed value data in the Store changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useValues hook).

Unlike the addValuesListener method, which returns a listener Id and requires you to remove it manually, the useValuesListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useValuesListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useValuesListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useValuesListener(() => console.log('Values changed'));
  return <span>App</span>;
};

const store = createStore().setValues({open: true});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().values);
// -> 1

store.setValue('open', false);
// -> 'Values changed'

root.unmount();
console.log(store.getListenerStats().values);
// -> 0
Since

v3.0.0

useDelValueCallback

The useDelValueCallback hook returns a parameterized callback that can be used to remove a single Value from a Store.

useDelValueCallback<Parameter>(
  valueId: string | GetId<Parameter>,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
valueIdstring | GetId<Parameter>

The Id of the Value in the Store to delete, or a GetId function that will return it.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store) => void

A function which is called after the deletion, with a reference to the Store.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of a GetId function if used as the valueId argument.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will delete data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the deletion.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the deletion to your application's undo stack.

The Store to which the callback will make the deletion (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useDelValueCallback hook to create an event handler which deletes from the Store when the span element is clicked.

import {useDelValueCallback, useValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setValues({open: true, employees: 3});
const App = () => {
  const handleClick = useDelValueCallback('open', store, () =>
    console.log('Deleted'),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useValues(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"open":true,"employees":3}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Deleted'

console.log(span.innerHTML);
// -> '{"employees":3}'
Since

v3.0.0

useHasValue

The useHasValue hook returns a boolean indicating whether a given Value exists in the Store, and registers a listener so that any changes to that result will cause a re-render.

useHasValue(
  valueId: string,
  storeOrStoreId?: StoreOrStoreId,
): boolean
TypeDescription
valueIdstring

The Id of the Value in the Store.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsboolean

Whether a Value with that Id exists in the Store.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useHasValue hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Value will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useHasValue hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useHasValue} from 'tinybase/ui-react';

const store = createStore().setValue('open', true);
const App = () => (
  <span>{JSON.stringify(useHasValue('employees', store))}</span>
);

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>false</span>'

store.setValue('employees', 3);
console.log(app.innerHTML);
// -> '<span>true</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useHasValue hook.

import {Provider, useHasValue} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useHasValue('employees'))}</span>;

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useHasValue hook.

import {Provider, useHasValue} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useHasValue('employees', 'petStore'))}</span>
);

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>false</span>'
Since

v4.4.0

useHasValueListener

The useHasValueListener hook registers a listener function with the Store that will be called when a Value is added to or removed from the Store.

useHasValueListener(
  valueId: IdOrNull,
  listener: HasValueListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
valueIdIdOrNull

The Id of the Value to listen to, or null as a wildcard.

listenerHasValueListener

The function that will be called whenever the matching Value is added or removed.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useHasValue hook).

You can either listen to a single Value being added or removed (by specifying the Value Id) or any Value being added or removed (by providing a null wildcard).

Unlike the addHasValueListener method, which returns a listener Id and requires you to remove it manually, the useHasValueListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useHasValueListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useHasValueListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useHasValueListener('open', () =>
    console.log('Value existence changed'),
  );
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().hasValue);
// -> 1

store.setValue('open', false);
// -> 'Value existence changed'

root.unmount();
console.log(store.getListenerStats().hasValue);
// -> 0
Since

v4.4.0

useSetValueCallback

The useSetValueCallback hook returns a parameterized callback that can be used to set the data of a single Value in a Store.

useSetValueCallback<Parameter>(
  valueId: string | GetId<Parameter>,
  getValue: (parameter: Parameter, store: Store) => Value | MapValue,
  getValueDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
  then?: (store: Store, value: Value | MapValue) => void,
  thenDeps?: DependencyList,
): ParameterizedCallback<Parameter>
TypeDescription
valueIdstring | GetId<Parameter>

The Id of the Value in the Store to set, or a GetId function that will return it.

getValue(parameter: Parameter, store: Store) => Value | MapValue

A function which returns the Value object that will be used to update the Store, based on the parameter the callback will receive (and which is most likely a DOM event).

getValueDeps?DependencyList

An optional array of dependencies for the getValue function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array. Also use this to indicate the dependencies of a GetId function if used as the valueId argument.

storeOrStoreId?StoreOrStoreId

The Store to be updated: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

then?(store: Store, value: Value | MapValue) => void

A function which is called after the mutation, with a reference to the Store and the Value used in the update.

thenDeps?DependencyList

An optional array of dependencies for the then function, which, if any change, result in the regeneration of the callback. This parameter defaults to an empty array.

returnsParameterizedCallback<Parameter>

A parameterized callback for subsequent use.

This hook is useful, for example, when creating an event handler that will mutate the data in the Store. In this case, the parameter will likely be the event, so that you can use data from it as part of the mutation.

The second parameter is a function which will produce the Value object that will then be used to update the Store in the callback.

If that function has any other dependencies, the changing of which should also cause the callback to be recreated, you can provide them in an array in the optional third parameter, just as you would for any React hook with dependencies.

For convenience, you can optionally provide a then function (with its own set of dependencies) which will be called just after the Store has been updated. This is a useful place to call the addCheckpoint method, for example, if you wish to add the mutation to your application's undo stack.

The Store to which the callback will make the mutation (indicated by the hook's storeOrStoreId parameter) is always automatically used as a hook dependency for the callback.

Example

This example uses the useSetValueCallback hook to create an event handler which updates the Store when the span element is clicked.

import {useSetValueCallback, useValues} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setValue('open', true);
const App = () => {
  const handleClick = useSetValueCallback(
    'bubbles',
    (e) => e.bubbles,
    [],
    store,
    (store, value) => console.log(`Updated: ${JSON.stringify(value)}`),
  );
  return (
    <span id="span" onClick={handleClick}>
      {JSON.stringify(useValues(store))}
    </span>
  );
};

const app = document.createElement('div');
createRoot(app).render(<App />);
const span = app.querySelector('span');
console.log(span.innerHTML);
// -> '{"open":true}'

// User clicks the <span> element:
// -> span MouseEvent('click', {bubbles: true})
// -> 'Updated: true'

console.log(span.innerHTML);
// -> '{"open":true,"bubbles":true}'
Since

v3.0.0

useValue

The useValue hook returns an object containing the data of a single Value in a Store, and registers a listener so that any changes to that result will cause a re-render.

useValue(
  valueId: string,
  storeOrStoreId?: StoreOrStoreId,
): ValueOrUndefined
TypeDescription
valueIdstring

The Id of the Value in the Store.

storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsValueOrUndefined

An object containing the entire data of the Value.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useValue hook lets you indicate which Store to get data for: omit the final optional final parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Value will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useValue hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useValue} from 'tinybase/ui-react';

const store = createStore().setValue('open', true);
const App = () => <span>{JSON.stringify(useValue('open', store))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>true</span>'

store.setValue('open', false);
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useValue hook.

import {Provider, useValue} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useValue('open'))}</span>;

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>true</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useValue hook.

import {Provider, useValue} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{JSON.stringify(useValue('open', 'petStore'))}</span>
);

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>true</span>'
Since

v3.0.0

useValueIds

The useValueIds hook returns the Ids of every Value in a Store, and registers a listener so that any changes to that result will cause a re-render.

useValueIds(storeOrStoreId?: StoreOrStoreId): Ids
TypeDescription
storeOrStoreId?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsIds

An array of the Ids of every Value in the Store.

A Provider component is used to wrap part of an application in a context, and it can contain a default Store or a set of Store objects named by Id. The useValueIds hook lets you indicate which Store to get data for: omit the optional parameter for the default context Store, provide an Id for a named context Store, or provide a Store explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Value Ids will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Store outside the application, which is used in the useValueIds hook by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {useValueIds} from 'tinybase/ui-react';

const store = createStore().setValue('open', true);
const App = () => <span>{JSON.stringify(useValueIds(store))}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>["open"]</span>'

store.setValue('employees', 3);
console.log(app.innerHTML);
// -> '<span>["open","employees"]</span>'

This example creates a Provider context into which a default Store is provided. A component within it then uses the useValueIds hook.

import {Provider, useValueIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useValueIds())}</span>;

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["open"]</span>'

This example creates a Provider context into which a Store is provided, named by Id. A component within it then uses the useValueIds hook.

import {Provider, useValueIds} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider storesById={{petStore: store}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{JSON.stringify(useValueIds('petStore'))}</span>;

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>["open"]</span>'
Since

v3.0.0

useValueIdsListener

The useValueIdsListener hook registers a listener function with a Store that will be called whenever the Value Ids in it change.

useValueIdsListener(
  listener: ValueIdsListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerValueIdsListener

The function that will be called whenever the Value Ids in the Store change.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useValueIds hook).

Unlike the addValueIdsListener method, which returns a listener Id and requires you to remove it manually, the useValueIdsListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useValueIdsListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useValueIdsListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useValueIdsListener(() => console.log('Value Ids changed'));
  return <span>App</span>;
};

const store = createStore().setValues({open: true});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().valueIds);
// -> 1

store.setValue('employees', 3);
// -> 'Value Ids changed'

root.unmount();
console.log(store.getListenerStats().valueIds);
// -> 0
Since

v3.0.0

useValueListener

The useValueListener hook registers a listener function with a Store that will be called whenever data in a Value changes.

useValueListener(
  valueId: IdOrNull,
  listener: ValueListener,
  listenerDeps?: DependencyList,
  mutator?: boolean,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
valueIdIdOrNull

The Id of the Value to listen to, or null as a wildcard.

listenerValueListener

The function that will be called whenever data in the Value changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

mutator?boolean

An optional boolean that indicates that the listener mutates Store data.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useValue hook).

You can either listen to a single Value (by specifying its Id as the method's first parameter) or changes to any Value (by providing a null wildcard).

Unlike the addValueListener method, which returns a listener Id and requires you to remove it manually, the useValueListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useValueListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useValueListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useValueListener('open', () => console.log('Value changed'));
  return <span>App</span>;
};

const store = createStore().setValues({open: true});
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().value);
// -> 1

store.setValue('open', false);
// -> 'Value changed'

root.unmount();
console.log(store.getListenerStats().value);
// -> 0
Since

v3.0.0

useDidFinishTransactionListener

The useDidFinishTransactionListener hook registers a listener function with a Store that will be called just after other non-mutating listeners are called at the end of the transaction.

useDidFinishTransactionListener(
  listener: TransactionListener,
  listenerDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerTransactionListener

The function that will be called after the end of a transaction.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

Unlike the addDidFinishTransactionListener method, which returns a listener Id and requires you to remove it manually, the useDidFinishTransactionListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useDidFinishTransactionListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {
  Provider,
  useDidFinishTransactionListener,
} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useDidFinishTransactionListener(() =>
    console.log('Did finish transaction'),
  );
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().transaction);
// -> 1

store.setValue('open', false);
// -> 'Did finish transaction'

root.unmount();
console.log(store.getListenerStats().transaction);
// -> 0
Since

v4.2.2

useStartTransactionListener

The useStartTransactionListener hook registers a listener function with the Store that will be called at the start of a transaction.

useStartTransactionListener(
  listener: TransactionListener,
  listenerDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerTransactionListener

The function that will be called at the start of a transaction.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

Unlike the addStartTransactionListener method, which returns a listener Id and requires you to remove it manually, the useStartTransactionListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useStartTransactionListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {Provider, useStartTransactionListener} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useStartTransactionListener(() => console.log('Start transaction'));
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().transaction);
// -> 1

store.setValue('open', false);
// -> 'Start transaction'

root.unmount();
console.log(store.getListenerStats().transaction);
// -> 0
Since

v4.2.2

useWillFinishTransactionListener

The useWillFinishTransactionListener hook registers a listener function with a Store that will be called just before other non-mutating listeners are called at the end of the transaction.

useWillFinishTransactionListener(
  listener: TransactionListener,
  listenerDeps?: DependencyList,
  storeOrStoreId?: StoreOrStoreId,
): void
TypeDescription
listenerTransactionListener

The function that will be called before the end of a transaction.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

storeOrStoreId?StoreOrStoreId

The Store to register the listener with: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

returnsvoid

This has no return value.

Unlike the addWillFinisTransactionListener method, which returns a listener Id and requires you to remove it manually, the useWillFinishTransactionListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Store will be deleted.

Example

This example uses the useWillFinishTransactionListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Store.

import {
  Provider,
  useWillFinishTransactionListener,
} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useWillFinishTransactionListener(() =>
    console.log('Will finish transaction'),
  );
  return <span>App</span>;
};

const store = createStore();
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} />);
console.log(store.getListenerStats().transaction);
// -> 1

store.setValue('open', false);
// -> 'Will finish transaction'

root.unmount();
console.log(store.getListenerStats().transaction);
// -> 0
Since

v4.2.2

Synchronizer hooks

This is the collection of synchronizer hooks within the ui-react module. There are 6 synchronizer hooks in total.

useCreateSynchronizer

The useCreateSynchronizer hook is used to create a Synchronizer within a React application along with convenient memoization and callbacks.

useCreateSynchronizer<SynchronizerOrUndefined>(
  store: undefined | MergeableStore,
  create: (store: MergeableStore) => Promise<SynchronizerOrUndefined>,
  createDeps?: DependencyList,
  destroy?: (synchronizer: Synchronizer) => void,
  destroyDeps?: DependencyList,
): SynchronizerOrUndefined
TypeDescription
storeundefined | MergeableStore

A reference to the MergeableStore for which to create a new Synchronizer object.

create(store: MergeableStore) => Promise<SynchronizerOrUndefined>

An asynchronous function for performing the creation steps of the Synchronizer object for the Store.

createDeps?DependencyList

An optional array of dependencies for the create function, which, if any change, result in its rerun. This parameter defaults to an empty array.

destroy?(synchronizer: Synchronizer) => void

An optional callback whenever the Synchronizer is destroyed due to a change in the createDeps dependencies.

destroyDeps?DependencyList

An optional array of dependencies for the destroy callback, which, if any change, result in destroy and then being rerun. This parameter defaults to an empty array.

returnsSynchronizerOrUndefined

A reference to the Synchronizer.

It is possible to create a Synchronizer outside of the React app with the regular createSynchronizer function and pass it in, but you may prefer to create it within the app, perhaps inside the top-level component. To prevent a new Synchronizer being created every time the app renders or re-renders, the useCreateSynchronizer hook performs the creation in an effect.

If your asynchronous create function (the second parameter to the hook) contains dependencies, the changing of which should cause the Synchronizer to be recreated, you can provide them in an array in the third parameter, just as you would for any React hook with dependencies. The MergeableStore passed in as the first parameter of this hook is used as a dependency by default.

The create function can return undefined, meaning that you can enable or disable synchronization conditionally within this hook. This is useful for applications which might turn on or off their cloud synchronization or collaboration features.

This hook ensures the Synchronizer object is destroyed whenever a new one is created or the component is unmounted.

Examples

This example creates a Synchronizer at the top level of a React application. Even though the App component is rendered twice, the Synchronizer creation only occurs once by default.

import {
  useCreateMergeableStore,
  useCreateSynchronizer,
  useTables,
} from 'tinybase/ui-react';
import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store = useCreateMergeableStore(() => createMergeableStore('s1'));
  useCreateSynchronizer(store, async (store) => {
    console.log('Synchronizer created');
    return await createLocalSynchronizer(store, 'pets');
  });
  return <span>{JSON.stringify(useTables(store))}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
// -> 'Synchronizer created'

// ...
root.render(<App />);
// No second Synchronizer creation

root.unmount();

This example creates a Synchronizer at the top level of a React application. The App component is rendered twice, each with a different top-level prop. The useCreateSynchronizer hook takes the url prop as a dependency, and so the Synchronizer object is created again on the second render. The first is destroyed and the destroy parameter is called for it. A then parameter is provided to start both Synchronizers' synchronization.

import {
  useCreateMergeableStore,
  useCreateSynchronizer,
  useTables,
} from 'tinybase/ui-react';
import React from 'react';
import {WebSocketServer} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

const server1 = createWsServer(new WebSocketServer({port: 8044}));
const server2 = createWsServer(new WebSocketServer({port: 8045}));

const App = ({url}) => {
  const store = useCreateMergeableStore(() => createMergeableStore('s1'));
  useCreateSynchronizer(
    store,
    async (store) => {
      const webSocket = new WebSocket(url);
      console.log(`Synchronizer created for ${webSocket.url}`);
      return await createWsSynchronizer(store, webSocket);
    },
    [url],
    (synchronizer) => {
      const webSocket = synchronizer.getWebSocket();
      console.log(`Synchronizer destroyed for ${webSocket.url}`);
    },
  );
  return <span>{JSON.stringify(useTables(store))}</span>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App url="ws://localhost:8044/" />);
// ...
// -> 'Synchronizer created for ws://localhost:8044/'

root.render(<App url="ws://localhost:8045/" />);
// ...
// -> 'Synchronizer created for ws://localhost:8045/'
// -> 'Synchronizer destroyed for ws://localhost:8044/'

root.unmount();
// -> 'Synchronizer destroyed for ws://localhost:8045/'

server1.destroy();
server2.destroy();
Since

v5.0.0

useSynchronizer

The useSynchronizer hook is used to get a reference to a Synchronizer object from within a Provider component context.

useSynchronizer(id?: string): Synchronizer | undefined
TypeDescription
id?string

An optional Id for accessing a Synchronizer object that was named with an Id in the Provider.

returnsSynchronizer | undefined

A reference to the Synchronizer object (or undefined if not within a Provider context, or if the requested Synchronizer object does not exist).

A Provider component is used to wrap part of an application in a context. It can contain a default Synchronizer object (or a set of Synchronizer objects named by Id) that can be easily accessed without having to be passed down as props through every component.

The useSynchronizer hook lets you either get a reference to the default Synchronizer object (when called without a parameter), or one of the Synchronizer objects that are named by Id (when called with an Id parameter).

Examples

This example creates a Provider context into which a default Synchronizer object is provided. A component within it then uses the useSynchronizer hook to get a reference to the Synchronizer object again, without the need to have it passed as a prop.

import {Provider, useSynchronizer} from 'tinybase/ui-react';
import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';

const App = ({synchronizer}) => (
  <Provider synchronizer={synchronizer}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useSynchronizer().getStatus()}</span>;

const synchronizer = createLocalSynchronizer(createMergeableStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App synchronizer={synchronizer} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Synchronizer object is provided, named by Id. A component within it then uses the useSynchronizer hook with that Id to get a reference to the Synchronizer object again, without the need to have it passed as a prop.

import {Provider, useSynchronizer} from 'tinybase/ui-react';
import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';

const App = ({synchronizer}) => (
  <Provider synchronizersById={{petSynchronizer: synchronizer}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>{useSynchronizer('petSynchronizer').getStatus()}</span>
);

const synchronizer = createLocalSynchronizer(createMergeableStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App synchronizer={synchronizer} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v5.3.0

useSynchronizerIds

The useSynchronizerIds hook is used to retrieve the Ids of all the named Synchronizer objects present in the current Provider component context.

useSynchronizerIds(): Ids
returnsIds

A list of the Ids in the context.

Example

This example adds two named Synchronizer objects to a Provider context and an inner component accesses their Ids.

import {
  Provider,
  useCreateMergeableStore,
  useCreateSynchronizer,
  useSynchronizerIds,
} from 'tinybase/ui-react';
import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';

const App = () => {
  const store1 = useCreateMergeableStore(createMergeableStore);
  const synchronizer1 = useCreateSynchronizer(
    store1,
    createLocalSynchronizer,
  );
  const store2 = useCreateMergeableStore(createMergeableStore);
  const synchronizer2 = useCreateSynchronizer(
    store2,
    createLocalSynchronizer,
  );
  return (
    <Provider synchronizersById={{synchronizer1, synchronizer2}}>
      <Pane />
    </Provider>
  );
};
const Pane = () => <span>{JSON.stringify(useSynchronizerIds())}</span>;

const app = document.createElement('div');
createRoot(app).render(<App />);

// ...
console.log(app.innerHTML);
// -> '<span>["synchronizer1","synchronizer2"]</span>'
Since

v5.3.0

useSynchronizerOrSynchronizerById

The useSynchronizerOrSynchronizerById hook is used to get a reference to a Synchronizer object from within a Provider component context, or have it passed directly to this hook.

useSynchronizerOrSynchronizerById(synchronizerOrSynchronizerId?: SynchronizerOrSynchronizerId): Synchronizer | undefined
TypeDescription
synchronizerOrSynchronizerId?SynchronizerOrSynchronizerId

Either an Id for accessing a Synchronizer object that was named with an Id in the Provider, or the Synchronizer object itself.

returnsSynchronizer | undefined

A reference to the Synchronizer object (or undefined if not within a Provider context, or if the requested Synchronizer object does not exist).

This is mostly of use when you are developing a component that needs a Synchronizer object and which might have been passed in explicitly to the component or is to be picked up from the context by Id (a common pattern for Synchronizer-based components).

This is unlikely to be used often. For most situations, you will want to use the useSynchronizer hook.

Example

This example creates a Provider context into which a default Synchronizer object is provided. A component within it then uses the useSynchronizerOrSynchronizerById hook to get a reference to the Synchronizer object again, without the need to have it passed as a prop. Note however, that unlike the useSynchronizer hook example, this component would also work if you were to pass the Synchronizer object directly into it, making it more portable.

import {
  Provider,
  useSynchronizerOrSynchronizerById,
} from 'tinybase/ui-react';
import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';

const App = ({synchronizer}) => (
  <Provider synchronizer={synchronizer}>
    <Pane />
  </Provider>
);
const Pane = ({synchronizer}) => (
  <span>
    {useSynchronizerOrSynchronizerById(synchronizer).getStatus()}
  </span>
);

const synchronizer = createLocalSynchronizer(createMergeableStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App synchronizer={synchronizer} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v5.3.0

useSynchronizerStatus

The useSynchronizerStatus hook returns a the status of a Synchronizer, and registers a listener so that any changes to it will cause a re-render.

useSynchronizerStatus(synchronizerOrSynchronizerId?: SynchronizerOrSynchronizerId): Status
TypeDescription
synchronizerOrSynchronizerId?SynchronizerOrSynchronizerId

The Synchronizer to be accessed: omit for the default context Synchronizer, provide an Id for a named context Synchronizer, or provide an explicit reference.

returnsStatus

The status of the Synchronizer: 0 means idle, 1 means loading, and 2 means saving.

A Provider component is used to wrap part of an application in a context, and it can contain a default Synchronizer or a set of Synchronizer objects named by Id. The useSynchronizerStatus hook lets you indicate which Synchronizer to get data for: omit the optional parameter for the default context Synchronizer, provide an Id for a named context Synchronizer, or provide a Synchronizer explicitly by reference.

When first rendered, this hook will create a listener so that changes to the Synchronizer status will cause a re-render. When the component containing this hook is unmounted, the listener will be automatically removed.

Examples

This example creates a Synchronizer outside the application, which is used in the useSynchronizerStatus hook by reference. A change to the status of the Synchronizer re-renders the component.

import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';
import {useSynchronizerStatus} from 'tinybase/ui-react';

const synchronizer = createLocalSynchronizer(createMergeableStore());
const App = () => <span>{useSynchronizerStatus(synchronizer)}</span>;

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a default Synchronizer is provided. A component within it then uses the useSynchronizerStatus hook.

import {Provider, useSynchronizerStatus} from 'tinybase/ui-react';
import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';

const App = ({synchronizer}) => (
  <Provider synchronizer={synchronizer}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useSynchronizerStatus()}</span>;

const synchronizer = createLocalSynchronizer(createMergeableStore());
const app = document.createElement('div');
createRoot(app).render(<App synchronizer={synchronizer} />);
console.log(app.innerHTML);
// -> '<span>0</span>'

This example creates a Provider context into which a Synchronizer is provided, named by Id. A component within it then uses the useSynchronizerStatus hook.

import {Provider, useSynchronizerStatus} from 'tinybase/ui-react';
import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';

const App = ({synchronizer}) => (
  <Provider synchronizersById={{petSynchronizer: synchronizer}}>
    <Pane />
  </Provider>
);
const Pane = () => <span>{useSynchronizerStatus('petSynchronizer')}</span>;

const synchronizer = createLocalSynchronizer(createMergeableStore());
const app = document.createElement('div');
createRoot(app).render(<App synchronizer={synchronizer} />);
console.log(app.innerHTML);
// -> '<span>0</span>'
Since

v5.3.0

useSynchronizerStatusListener

The useSynchronizerStatusListener hook registers a listener function with the Synchronizer that will be called when its status changes.

useSynchronizerStatusListener(
  listener: StatusListener<MergeableStoreOnly>,
  listenerDeps?: DependencyList,
  synchronizerOrSynchronizerId?: SynchronizerOrSynchronizerId,
): void
TypeDescription
listenerStatusListener<MergeableStoreOnly>

The function that will be called whenever the status of the Synchronizer changes.

listenerDeps?DependencyList

An optional array of dependencies for the listener function, which, if any change, result in the re-registration of the listener. This parameter defaults to an empty array.

synchronizerOrSynchronizerId?SynchronizerOrSynchronizerId

The Synchronizer to be accessed: omit for the default context Synchronizer, provide an Id for a named context Synchronizer, or provide an explicit reference.

returnsvoid

This has no return value.

This hook is useful for situations where a component needs to register its own specific listener to do more than simply tracking the value (which is more easily done with the useSynchronizerStatus hook).

Unlike the addStatusListener method, which returns a listener Id and requires you to remove it manually, the useSynchronizerStatusListener hook manages this lifecycle for you: when the listener changes (per its listenerDeps dependencies) or the component unmounts, the listener on the underlying Synchronizer will be deleted.

Example

This example uses the useSynchronizerStatusListener hook to create a listener that is scoped to a single component. When the component is unmounted, the listener is removed from the Synchronizer.

import {Provider, useSynchronizerStatusListener} from 'tinybase/ui-react';
import React from 'react';
import {createLocalSynchronizer} from 'tinybase/synchronizers/synchronizer-local';
import {createMergeableStore} from 'tinybase';
import {createRoot} from 'react-dom/client';

const App = ({synchronizer}) => (
  <Provider synchronizer={synchronizer}>
    <Pane />
  </Provider>
);
const Pane = () => {
  useSynchronizerStatusListener((synchronizer, status) =>
    console.log('Synchronizer status changed: ' + status),
  );
  return <span>App</span>;
};

const synchronizer = createLocalSynchronizer(createMergeableStore());
const app = document.createElement('div');
const root = createRoot(app);
root.render(<App synchronizer={synchronizer} />);

synchronizer.load();
// -> 'Synchronizer status changed: 1'
// ...
// -> 'Synchronizer status changed: 0'

synchronizer.save();
// -> 'Synchronizer status changed: 2'
// ...
// -> 'Synchronizer status changed: 0'
Since

v5.3.0

Checkpoints components

This is the collection of checkpoints components within the ui-react module. There are 4 checkpoints components in total.

BackwardCheckpointsView

The BackwardCheckpointsView component renders a list of previous checkpoints that the underlying Store can go back to.

BackwardCheckpointsView(props: BackwardCheckpointsProps): ComponentReturnType
TypeDescription
propsBackwardCheckpointsProps

The props for this component.

returnsComponentReturnType

A rendering of the previous checkpoints, if present.

The component's props identify which previous checkpoints to render based on the Checkpoints object (which is either the default context Checkpoints object, a named context Checkpoints object, or an explicit reference).

This component renders a list by iterating over each checkpoints. By default these are in turn rendered with the CheckpointView component, but you can override this behavior by providing a checkpointComponent prop, a custom component of your own that will render a checkpoint based on CheckpointProps. You can also pass additional props to your custom component with the getCheckpointComponentProps callback prop.

This component uses the useCheckpointIds hook under the covers, which means that any changes to the checkpoint Ids in the Checkpoints object will cause a re-render.

Examples

This example creates a Checkpoints object outside the application, which is used in the BackwardCheckpointsView component by reference to render a list of previous checkpoints.

import {createCheckpoints, createStore} from 'tinybase';
import {BackwardCheckpointsView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);
const App = () => (
  <div>
    <BackwardCheckpointsView checkpoints={checkpoints} separator="/" />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div></div>'

checkpoints.setCheckpoint('0', 'initial');
store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');
console.log(app.innerHTML);
// -> '<div>initial</div>'

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(app.innerHTML);
// -> '<div>initial/identified</div>'

This example creates a Provider context into which a default Checkpoints object is provided. The BackwardCheckpointsView component within it then renders the list of previous checkpoints (with Ids for readability).

import {BackwardCheckpointsView, Provider} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <BackwardCheckpointsView debugIds={true} />
  </div>
);

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);

checkpoints.setCheckpoint('0', 'initial');
store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<div>0:{initial}1:{identified}</div>'

This example creates a Provider context into which a default Checkpoints object is provided. The BackwardCheckpointsView component within it then renders the list of previous checkpoints with a custom Row component and a custom props callback.

import {
  BackwardCheckpointsView,
  CheckpointView,
  Provider,
} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const getBoldProp = (checkpointId) => ({bold: checkpointId == '0'});
const Pane = () => (
  <div>
    <BackwardCheckpointsView
      checkpointComponent={FormattedCheckpointView}
      getCheckpointComponentProps={getBoldProp}
    />
  </div>
);
const FormattedCheckpointView = ({checkpoints, checkpointId, bold}) => (
  <span>
    {bold ? <b>{checkpointId}</b> : checkpointId}
    {': '}
    <CheckpointView
      checkpoints={checkpoints}
      checkpointId={checkpointId}
    />
  </span>
);

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);

checkpoints.setCheckpoint('0', 'initial');
store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<div><span><b>0</b>: initial</span><span>1: identified</span></div>'
Since

v1.0.0

CheckpointView

The CheckpointView component simply renders the label of a checkpoint.

CheckpointView(props: CheckpointProps): ComponentReturnType
TypeDescription
propsCheckpointProps

The props for this component.

returnsComponentReturnType

A rendering of the checkpoint: its label if present, or Id.

The component's props identify which checkpoint to render based on Checkpoint Id and Checkpoints object (which is either the default context Checkpoints object, a named context Checkpoints object, or an explicit reference).

The primary purpose of this component is to render multiple checkpoints in a BackwardCheckpointsView component or ForwardCheckpointsView component.

This component uses the useCheckpoint hook under the covers, which means that any changes to the local Row Ids in the Relationship will cause a re-render.

Example

This example creates a Checkpoints object outside the application, which is used in the CheckpointView component by reference to render a checkpoint with a label (with its Id for readability).

import {createCheckpoints, createStore} from 'tinybase';
import {CheckpointView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {fido: {species: 'dog'}});
const checkpoints = createCheckpoints(store);
const App = () => (
  <div>
    <CheckpointView
      checkpointId="1"
      checkpoints={checkpoints}
      debugIds={true}
    />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>1:{}</div>'

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(app.innerHTML);
// -> '<div>1:{sale}</div>'

checkpoints.setCheckpoint('1', 'sold');
console.log(app.innerHTML);
// -> '<div>1:{sold}</div>'
Since

v1.0.0

CurrentCheckpointView

The CurrentCheckpointView component renders the current checkpoint that the underlying Store is currently on.

CurrentCheckpointView(props: CurrentCheckpointProps): ComponentReturnType
TypeDescription
propsCurrentCheckpointProps

The props for this component.

returnsComponentReturnType

A rendering of the current checkpoint, if present.

The component's props identify which current checkpoint to render based on the Checkpoints object (which is either the default context Checkpoints object, a named context Checkpoints object, or an explicit reference).

By default the current checkpoint is rendered with the CheckpointView component, but you can override this behavior by providing a checkpointComponent prop, a custom component of your own that will render a checkpoint based on CheckpointProps. You can also pass additional props to your custom component with the getCheckpointComponentProps callback prop.

This component uses the useCheckpointIds hook under the covers, which means that any changes to the current checkpoint Id in the Checkpoints object will cause a re-render.

Examples

This example creates a Checkpoints object outside the application, which is used in the CurrentCheckpointView component by reference to render the current checkpoints.

import {createCheckpoints, createStore} from 'tinybase';
import {CurrentCheckpointView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);
const App = () => (
  <div>
    <CurrentCheckpointView checkpoints={checkpoints} />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div></div>'

store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');
console.log(app.innerHTML);
// -> '<div>identified</div>'

store.setCell('pets', 'fido', 'sold', true);
console.log(app.innerHTML);
// -> '<div></div>'

checkpoints.addCheckpoint('sale');
console.log(app.innerHTML);
// -> '<div>sale</div>'

This example creates a Provider context into which a default Checkpoints object is provided. The CurrentCheckpointView component within it then renders current checkpoint (with its Id for readability).

import {CurrentCheckpointView, Provider} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <CurrentCheckpointView debugIds={true} />
  </div>
);

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);

store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<div>1:{identified}</div>'

This example creates a Provider context into which a default Checkpoints object is provided. The CurrentCheckpointView component within it then renders the list of future checkpoints with a custom Row component and a custom props callback.

import {
  CheckpointView,
  CurrentCheckpointView,
  Provider,
} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const getBoldProp = (checkpointId) => ({bold: checkpointId == '1'});
const Pane = () => (
  <div>
    <CurrentCheckpointView
      checkpointComponent={FormattedCheckpointView}
      getCheckpointComponentProps={getBoldProp}
    />
  </div>
);
const FormattedCheckpointView = ({checkpoints, checkpointId, bold}) => (
  <span>
    {bold ? <b>{checkpointId}</b> : checkpointId}
    {': '}
    <CheckpointView
      checkpoints={checkpoints}
      checkpointId={checkpointId}
    />
  </span>
);

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);

store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<div><span><b>1</b>: identified</span></div>'

store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
console.log(app.innerHTML);
// -> '<div><span>2: sale</span></div>'
Since

v1.0.0

ForwardCheckpointsView

The ForwardCheckpointsView component renders a list of future checkpoints that the underlying Store can go forwards to.

ForwardCheckpointsView(props: ForwardCheckpointsProps): ComponentReturnType
TypeDescription
propsForwardCheckpointsProps

The props for this component.

returnsComponentReturnType

A rendering of the future checkpoints, if present.

The component's props identify which future checkpoints to render based on the Checkpoints object (which is either the default context Checkpoints object, a named context Checkpoints object, or an explicit reference).

This component renders a list by iterating over each checkpoints. By default these are in turn rendered with the CheckpointView component, but you can override this behavior by providing a checkpointComponent prop, a custom component of your own that will render a checkpoint based on CheckpointProps. You can also pass additional props to your custom component with the getCheckpointComponentProps callback prop.

This component uses the useCheckpointIds hook under the covers, which means that any changes to the checkpoint Ids in the Checkpoints object will cause a re-render.

Examples

This example creates a Checkpoints object outside the application, which is used in the ForwardCheckpointsView component by reference to render a list of future checkpoints.

import {createCheckpoints, createStore} from 'tinybase';
import {ForwardCheckpointsView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);
const App = () => (
  <div>
    <ForwardCheckpointsView checkpoints={checkpoints} separator="/" />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div></div>'

store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
checkpoints.goBackward();
console.log(app.innerHTML);
// -> '<div>sale</div>'

checkpoints.goBackward();
console.log(app.innerHTML);
// -> '<div>identified/sale</div>'

This example creates a Provider context into which a default Checkpoints object is provided. The ForwardCheckpointsView component within it then renders the list of future checkpoints (with Ids for readability).

import {ForwardCheckpointsView, Provider} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <ForwardCheckpointsView debugIds={true} />
  </div>
);

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);

store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
checkpoints.goTo('0');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<div>1:{identified}2:{sale}</div>'

This example creates a Provider context into which a default Checkpoints object is provided. The ForwardCheckpointsView component within it then renders the list of future checkpoints with a custom Row component and a custom props callback.

import {
  CheckpointView,
  ForwardCheckpointsView,
  Provider,
} from 'tinybase/ui-react';
import {createCheckpoints, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({checkpoints}) => (
  <Provider checkpoints={checkpoints}>
    <Pane />
  </Provider>
);
const getBoldProp = (checkpointId) => ({bold: checkpointId == '1'});
const Pane = () => (
  <div>
    <ForwardCheckpointsView
      checkpointComponent={FormattedCheckpointView}
      getCheckpointComponentProps={getBoldProp}
    />
  </div>
);
const FormattedCheckpointView = ({checkpoints, checkpointId, bold}) => (
  <span>
    {bold ? <b>{checkpointId}</b> : checkpointId}
    {': '}
    <CheckpointView
      checkpoints={checkpoints}
      checkpointId={checkpointId}
    />
  </span>
);

const store = createStore().setTable('pets', {fido: {color: 'brown'}});
const checkpoints = createCheckpoints(store);

store.setCell('pets', 'fido', 'species', 'dog');
checkpoints.addCheckpoint('identified');
store.setCell('pets', 'fido', 'sold', true);
checkpoints.addCheckpoint('sale');
checkpoints.goTo('0');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App checkpoints={checkpoints} />);
console.log(app.innerHTML);
// -> '<div><span><b>1</b>: identified</span><span>2: sale</span></div>'
Since

v1.0.0

Context components

This is the collection of context components within the ui-react module. There is only one function, Provider.

Provider

The Provider component is used to wrap part of an application in a context that provides default objects to be used by hooks and components within.

Provider(props: ProviderProps & {children: ReactNode}): ComponentReturnType
TypeDescription
propsProviderProps & {children: ReactNode}

The props for this component.

returnsComponentReturnType

A rendering of the child components.

Store, Metrics, Indexes, Relationships, Queries, Checkpoints, Persister, and Synchronizer objects can be passed into the context of an application and used throughout. One of each type of object can be provided as a default within the context. Additionally, multiple of each type of object can be provided in an Id-keyed map to the ___ById props.

Provider contexts can be nested and the objects passed in will be merged. For example, if an outer context contains a default Metrics object and an inner context contains only a default Store, both the Metrics objects and the Store will be visible within the inner context. If the outer context contains a Store named by Id and the inner context contains a Store named by a different Id, both will be visible within the inner context.

Examples

This example creates a Provider context into which a Store and a Metrics object are provided, one by default, and one named by Id. Components within it then render content from both, without the need to have them passed as props.

import {CellView, Provider, useMetric} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({store, metrics}) => (
  <Provider store={store} metricsById={{petStore: metrics}}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    <CellView tableId="species" rowId="dog" cellId="price" />,
    <CellView tableId="species" rowId="cat" cellId="price" />,
    {useMetric('highestPrice', 'petStore')}
  </span>
);

const store = createStore();
store.setTable('species', {dog: {price: 5}, cat: {price: 4}});
const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App store={store} metrics={metrics} />);
console.log(app.innerHTML);
// -> '<span>5,4,5</span>'

This example creates nested Provider contexts into which Store and Metrics objects are provided, showing how visibility is merged.

import {
  CellView,
  Provider,
  useCreateStore,
  useMetric,
} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({petStore, metrics}) => (
  <Provider storesById={{pet: petStore}} metrics={metrics}>
    <OuterPane />
  </Provider>
);
const OuterPane = () => {
  const planetStore = useCreateStore(() =>
    createStore().setTables({planets: {mars: {moons: 2}}}),
  );
  return (
    <Provider storesById={{planet: planetStore}}>
      <InnerPane />
    </Provider>
  );
};
const InnerPane = () => (
  <span>
    <CellView tableId="species" rowId="dog" cellId="price" store="pet" />,
    {useMetric('highestPrice')},
    <CellView
      tableId="planets"
      rowId="mars"
      cellId="moons"
      store="planet"
    />
  </span>
);

const petStore = createStore();
petStore.setTable('species', {dog: {price: 5}, cat: {price: 4}});
const metrics = createMetrics(petStore);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App petStore={petStore} metrics={metrics} />);
console.log(app.innerHTML);
// -> '<span>5,5,2</span>'
Since

v1.0.0

Indexes components

This is the collection of indexes components within the ui-react module. There are only two indexes components, IndexView and SliceView.

IndexView

The IndexView component renders the contents of a Index, and registers a listener so that any changes to that result will cause a re-render.

IndexView(props: IndexProps): ComponentReturnType
TypeDescription
propsIndexProps

The props for this component.

returnsComponentReturnType

A rendering of the Index, or nothing, if not present.

The component's props identify which Index to render based on Index Id, and Indexes object (which is either the default context Indexes object, a named context Indexes object, or an explicit reference).

This component renders a Index by iterating over its Slice objects. By default these are in turn rendered with the SliceView component, but you can override this behavior by providing a sliceComponent prop, a custom component of your own that will render a Slice based on SliceProps. You can also pass additional props to your custom component with the getSliceComponentProps callback prop.

This component uses the useSliceIds hook under the covers, which means that any changes to the structure of the Index will cause a re-render.

Examples

This example creates an Indexes object outside the application, which is used in the IndexView component by reference. A change to the Slice Ids re-renders the component.

import {createIndexes, createStore} from 'tinybase';
import {IndexView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
const App = () => (
  <div>
    <IndexView indexId="bySpecies" indexes={indexes} separator="/" />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>dog/cat</div>'

store.setRow('pets', 'lowly', {species: 'worm'});
console.log(app.innerHTML);
// -> '<div>dog/cat/worm</div>'

This example creates a Provider context into which a default Indexes object is provided. The IndexView component within it then renders the Index (with Ids for readability).

import {IndexView, Provider} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <IndexView indexId="bySpecies" debugIds={true} />
  </div>
);

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<div>bySpecies:{dog:{fido:{species:{dog}}cujo:{species:{dog}}}}</div>'

This example creates a Provider context into which a default Indexes object is provided. The IndexView component within it then renders the Index with a custom Slice component and a custom props callback.

import {IndexView, Provider, SliceView} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const getBoldProp = (sliceId) => ({bold: sliceId == 'dog'});
const Pane = () => (
  <div>
    <IndexView
      indexId="bySpecies"
      sliceComponent={FormattedSliceView}
      getSliceComponentProps={getBoldProp}
    />
  </div>
);
const FormattedSliceView = ({indexId, sliceId, bold}) => (
  <span>
    {bold ? <b>{sliceId}</b> : sliceId}
    {': '}
    <SliceView indexId={indexId} sliceId={sliceId} separator="/" />
  </span>
);

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<div><span><b>dog</b>: dog/dog</span><span>cat: cat</span></div>'
Since

v1.0.0

SliceView

The SliceView component renders the contents of a Slice, and registers a listener so that any changes to that result will cause a re-render.

SliceView(props: SliceProps): ComponentReturnType
TypeDescription
propsSliceProps

The props for this component.

returnsComponentReturnType

A rendering of the Slice, or nothing, if not present.

The component's props identify which Slice to render based on Index Id, Slice Id, and Indexes object (which is either the default context Indexes object, a named context Indexes object, or an explicit reference).

This component renders a Slice by iterating over its Row objects. By default these are in turn rendered with the RowView component, but you can override this behavior by providing a rowComponent prop, a custom component of your own that will render a Row based on RowProps. You can also pass additional props to your custom component with the getRowComponentProps callback prop.

This component uses the useSliceRowIds hook under the covers, which means that any changes to the structure of the Slice will cause a re-render.

Examples

This example creates an Indexes object outside the application, which is used in the SliceView component by reference. A change to the Row Ids re-renders the component.

import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {SliceView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');
const App = () => (
  <div>
    <SliceView
      indexId="bySpecies"
      sliceId="dog"
      indexes={indexes}
      separator="/"
    />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>dog</div>'

store.setRow('pets', 'cujo', {species: 'dog'});
console.log(app.innerHTML);
// -> '<div>dog/dog</div>'

This example creates a Provider context into which a default Indexes object is provided. The SliceView component within it then renders the Slice (with Ids for readability).

import {Provider, SliceView} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <SliceView indexId="bySpecies" sliceId="dog" debugIds={true} />
  </div>
);

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<div>dog:{fido:{species:{dog}}cujo:{species:{dog}}}</div>'

This example creates a Provider context into which a default Indexes object is provided. The SliceView component within it then renders the Slice with a custom Row component and a custom props callback.

import {Provider, RowView, SliceView} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const getBoldProp = (rowId) => ({bold: rowId == 'fido'});
const Pane = () => (
  <div>
    <SliceView
      indexId="bySpecies"
      sliceId="dog"
      rowComponent={FormattedRowView}
      getRowComponentProps={getBoldProp}
    />
  </div>
);
const FormattedRowView = ({store, tableId, rowId, bold}) => (
  <span>
    {bold ? <b>{rowId}</b> : rowId}
    {': '}
    <RowView store={store} tableId={tableId} rowId={rowId} separator="/" />
  </span>
);

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// -> '<div><span><b>fido</b>: dog/brown</span><span>cujo: dog</span></div>'
Since

v1.0.0

Metrics components

This is the collection of metrics components within the ui-react module. There is only one function, MetricView.

MetricView

The MetricView component renders the current value of a Metric, and registers a listener so that any changes to that result will cause a re-render.

MetricView(props: MetricProps): ComponentReturnType
TypeDescription
propsMetricProps

The props for this component.

returnsComponentReturnType

A rendering of the Metric, or nothing, if not present.

The component's props can identify which Metrics object to get data for: omit the optional final parameter for the default context Metrics object, provide an Id for a named context Metrics object, or by explicit reference.

This component uses the useMetric hook under the covers, which means that any changes to the Metric will cause a re-render.

Examples

This example creates a Metrics object outside the application, which is used in the MetricView component hook by reference. A change to the Metric re-renders the component.

import {createMetrics, createStore} from 'tinybase';
import {MetricView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});
const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');
const App = () => (
  <div>
    <MetricView metricId="highestPrice" metrics={metrics} />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>5</div>'

store.setCell('species', 'horse', 'price', 20);
console.log(app.innerHTML);
// -> '<div>20</div>'

This example creates a Provider context into which a default Metrics object is provided. The MetricView component within it then renders the Metric (with its Id for readability).

import {MetricView, Provider} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({metrics}) => (
  <Provider metrics={metrics}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <MetricView metricId="highestPrice" debugIds={true} />
  </div>
);

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});
const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

const app = document.createElement('div');
createRoot(app).render(<App metrics={metrics} />);
console.log(app.innerHTML);
// -> '<div>highestPrice:{5}</div>'

This example creates a Provider context into which a default Metrics object is provided. The MetricView component within it then attempts to render a non-existent Metric.

import {MetricView, Provider} from 'tinybase/ui-react';
import {createMetrics, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({metrics}) => (
  <Provider metrics={metrics}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <MetricView metricId="lowestPrice" />
  </div>
);

const store = createStore().setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});
const metrics = createMetrics(store);
metrics.setMetricDefinition('highestPrice', 'species', 'max', 'price');

const app = document.createElement('div');
createRoot(app).render(<App metrics={metrics} />);
console.log(app.innerHTML);
// -> '<div></div>'
Since

v1.0.0

Queries components

This is the collection of queries components within the ui-react module. There are 4 queries components in total.

ResultSortedTableView

The ResultSortedTableView component renders the contents of a single query's sorted ResultTable in a Queries object, and registers a listener so that any changes to that result will cause a re-render.

ResultSortedTableView(props: ResultSortedTableProps): ComponentReturnType
TypeDescription
propsResultSortedTableProps

The props for this component.

returnsComponentReturnType

A rendering of the ResultTable, or nothing, if not present.

The component's props identify which ResultTable to render based on query Id, and Queries object (which is either the default context Queries object, a named context Queries object, or by explicit reference). It also takes a Cell Id to sort by and a boolean to indicate that the sorting should be in descending order. The offset and limit props are used to paginate results, but default to 0 and undefined to return all available Row Ids if not specified.

This component renders a ResultTable by iterating over its Row objects, in the order dictated by the sort parameters. By default these are in turn rendered with the ResultRowView component, but you can override this behavior by providing a resultRowComponent prop, a custom component of your own that will render a Row based on ResultRowProps. You can also pass additional props to your custom component with the getResultRowComponentProps callback prop.

This component uses the useResultSortedRowIds hook under the covers, which means that any changes to the structure or sorting of the ResultTable will cause a re-render.

Examples

This example creates a Queries object outside the application, which is used in the ResultSortedTableView component by reference. A change to the data in the Store re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {ResultSortedTableView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const App = () => (
  <div>
    <ResultSortedTableView
      queryId="petColors"
      cellId="color"
      queries={queries}
      separator="/"
    />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>black/brown</div>'

store.setCell('pets', 'felix', 'color', 'white');
console.log(app.innerHTML);
// -> '<div>brown/white</div>'

This example creates a Provider context into which a default Queries object is provided. The ResultSortedTableView component within it then renders the Table (with Ids for readability).

import {Provider, ResultSortedTableView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <ResultSortedTableView
      queryId="petColors"
      cellId="color"
      debugIds={true}
    />
  </div>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<div>petColors:{felix:{color:{black}}fido:{color:{brown}}}</div>'

This example creates a Provider context into which a default Queries object is provided. The ResultSortedTableView component within it then renders the Table with a custom Row component and a custom props callback.

import {
  Provider,
  ResultRowView,
  ResultSortedTableView,
} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const getBoldProp = (rowId) => ({bold: rowId == 'fido'});
const Pane = () => (
  <div>
    <ResultSortedTableView
      queryId="petColors"
      cellId="color"
      resultRowComponent={FormattedRowView}
      getResultRowComponentProps={getBoldProp}
    />
  </div>
);
const FormattedRowView = ({queryId, rowId, bold}) => (
  <span>
    {bold ? <b>{rowId}</b> : rowId}
    {': '}
    <ResultRowView queryId={queryId} rowId={rowId} />
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<div><span>felix: black</span><span><b>fido</b>: brown</span></div>'
Since

v2.0.0

ResultTableView

The ResultTableView component renders the contents of a single query's ResultTable in a Queries object, and registers a listener so that any changes to that result will cause a re-render.

ResultTableView(props: ResultTableProps): ComponentReturnType
TypeDescription
propsResultTableProps

The props for this component.

returnsComponentReturnType

A rendering of the ResultTable, or nothing, if not present.

The component's props identify which ResultTable to render based on query Id, and Queries object (which is either the default context Queries object, a named context Queries object, or by explicit reference).

This component renders a ResultTable by iterating over its Row objects. By default these are in turn rendered with the ResultRowView component, but you can override this behavior by providing a resultRowComponent prop, a custom component of your own that will render a Row based on ResultRowProps. You can also pass additional props to your custom component with the getResultRowComponentProps callback prop.

This component uses the useResultRowIds hook under the covers, which means that any changes to the structure of the ResultTable will cause a re-render.

Examples

This example creates a Queries object outside the application, which is used in the ResultTableView component by reference. A change to the data in the Store re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {ResultTableView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const App = () => (
  <div>
    <ResultTableView queryId="petColors" queries={queries} separator="/" />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>brown/black</div>'

store.setRow('pets', 'cujo', {species: 'dog', color: 'black'});
console.log(app.innerHTML);
// -> '<div>brown/black/black</div>'

This example creates a Provider context into which a default Queries object is provided. The ResultTableView component within it then renders the Table (with Ids for readability).

import {Provider, ResultTableView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <ResultTableView queryId="petColors" debugIds={true} />
  </div>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<div>petColors:{fido:{color:{brown}}felix:{color:{black}}}</div>'

This example creates a Provider context into which a default Queries object is provided. The ResultTableView component within it then renders the Table with a custom Row component and a custom props callback.

import {Provider, ResultRowView, ResultTableView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const getBoldProp = (rowId) => ({bold: rowId == 'fido'});
const Pane = () => (
  <div>
    <ResultTableView
      queryId="petColors"
      resultRowComponent={FormattedRowView}
      getResultRowComponentProps={getBoldProp}
    />
  </div>
);
const FormattedRowView = ({queryId, rowId, bold}) => (
  <span>
    {bold ? <b>{rowId}</b> : rowId}
    {': '}
    <ResultRowView queryId={queryId} rowId={rowId} />
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<div><span><b>fido</b>: brown</span><span>felix: black</span></div>'
Since

v2.0.0

ResultRowView

The ResultRowView component renders the contents of a single Row in a given query's ResultTable, and registers a listener so that any changes to that result will cause a re-render.

ResultRowView(props: ResultRowProps): ComponentReturnType
TypeDescription
propsResultRowProps

The props for this component.

returnsComponentReturnType

A rendering of the result Row, or nothing, if not present.

The component's props identify which Row to render based on query Id, Row Id, and Queries object (which is either the default context Queries object, a named context Queries object, or an explicit reference).

This component renders a Row by iterating over its Cell values. By default these are in turn rendered with the ResultCellView component, but you can override this behavior by providing a resultCellComponent prop, a custom component of your own that will render a Cell based on ResultCellProps. You can also pass additional props to your custom component with the getResultCellComponentProps callback prop.

You can create your own ResultRowView-like component to customize the way that a result Row is rendered: see the ResultTableView component for more details.

This component uses the useResultCellIds hook under the covers, which means that any changes to the structure of the result Row will cause a re-render.

Examples

This example creates a Queries object outside the application, which is used in the ResultRowView component by reference. A change to the data in the Store re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {ResultRowView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => {
    select('species');
    select('color');
  },
);
const App = () => (
  <div>
    <ResultRowView
      queryId="petColors"
      rowId="fido"
      queries={queries}
      separator="/"
    />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>dog/brown</div>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<div>dog/walnut</div>'

This example creates a Provider context into which a default Queries object is provided. The ResultRowView component within it then renders the Row (with Ids for readability).

import {Provider, ResultRowView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <ResultRowView queryId="petColors" rowId="fido" debugIds={true} />
  </div>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => {
  select('species');
  select('color');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<div>fido:{species:{dog}color:{brown}}</div>'

This example creates a Provider context into which a default Queries object is provided. The ResultRowView component within it then renders the Row with a custom Cell component and a custom props callback.

import {Provider, ResultCellView, ResultRowView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const getBoldProp = (cellId) => ({bold: cellId == 'species'});
const Pane = () => (
  <div>
    <ResultRowView
      queryId="petColors"
      rowId="fido"
      resultCellComponent={FormattedResultCellView}
      getResultCellComponentProps={getBoldProp}
    />
  </div>
);
const FormattedResultCellView = ({queryId, rowId, cellId, bold}) => (
  <span>
    {bold ? <b>{cellId}</b> : cellId}
    {': '}
    <ResultCellView queryId={queryId} rowId={rowId} cellId={cellId} />
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => {
  select('species');
  select('color');
});
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<div><span><b>species</b>: dog</span><span>color: brown</span></div>'
Since

v2.0.0

ResultCellView

The ResultCellView component renders the value of a single Cell in a given Row, in a given query's ResultTable, and registers a listener so that any changes to that result will cause a re-render.

ResultCellView(props: ResultCellProps): ComponentReturnType
TypeDescription
propsResultCellProps

The props for this component.

returnsComponentReturnType

A rendering of the result Cell, or nothing, if not present.

The component's props identify which Cell to render based on query Id, Row Id, Cell Id, and Queries object (which is either the default context Queries object, a named context Queries object, or an explicit reference).

A Cell contains a string, number, or boolean, so the value is rendered directly without further decoration. You can create your own ResultCellView-like component to customize the way that a Cell is rendered: see the ResultRowView component for more details.

This component uses the useResultCell hook under the covers, which means that any changes to the specified Cell will cause a re-render.

Examples

This example creates a Queries object outside the application, which is used in the ResultCellView component by reference. A change to the data in the Store re-renders the component.

import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {ResultCellView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});
const queries = createQueries(store).setQueryDefinition(
  'petColors',
  'pets',
  ({select}) => select('color'),
);
const App = () => (
  <span>
    <ResultCellView
      queryId="petColors"
      rowId="fido"
      cellId="color"
      queries={queries}
    />
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>brown</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>walnut</span>'

This example creates a Provider context into which a default Queries object is provided. The ResultCellView component within it then renders the Cell (with its Id for readability).

import {Provider, ResultCellView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    <ResultCellView
      queryId="petColors"
      rowId="fido"
      cellId="color"
      debugIds={true}
    />
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span>color:{brown}</span>'

This example creates a Provider context into which a default Queries object is provided. The ResultCellView component within it then attempts to render a non-existent Cell.

import {Provider, ResultCellView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    <ResultCellView queryId="petColors" rowId="fido" cellId="height" />
  </span>
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
    cujo: {species: 'dog', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// -> '<span></span>'
Since

v2.0.0

Relationships components

This is the collection of relationships components within the ui-react module. There are only three relationships components, LinkedRowsView, LocalRowsView, and RemoteRowView.

LinkedRowsView

The LinkedRowsView component renders the local Row objects for a given remote Row in a Relationship, and registers a listener so that any changes to that result will cause a re-render.

LinkedRowsView(props: LinkedRowsProps): ComponentReturnType
TypeDescription
propsLinkedRowsProps

The props for this component.

returnsComponentReturnType

A rendering of the local Row objects, or nothing, if not present.

The component's props identify which local Rows to render based on Relationship Id, remote Row Id, and Relationships object (which is either the default context Relationships object, a named context Relationships object, or an explicit reference).

By default the local Rows are rendered with the RowView component, but you can override this behavior by providing a rowComponent prop, a custom component of your own that will render the Row based on RowProps. You can also pass additional props to your custom component with the getRowComponentProps callback prop.

This component uses the useLocalRowIds hook under the covers, which means that any changes to the local Row Ids in the Relationship will cause a re-render.

Examples

This example creates a Relationships object outside the application, which is used in the LinkedRowsView component by reference. A change to the Row Ids re-renders the component.

import {createRelationships, createStore} from 'tinybase';
import {LinkedRowsView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const store = createStore().setTable('pets', {
  fido: {next: 'felix'},
  felix: {next: 'cujo'},
  cujo: {species: 'dog'},
});
const relationships = createRelationships(store).setRelationshipDefinition(
  'petSequence',
  'pets',
  'pets',
  'next',
);
const App = () => (
  <div>
    <LinkedRowsView
      relationshipId="petSequence"
      firstRowId="fido"
      relationships={relationships}
      separator="/"
    />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>felix/cujo/dog</div>'

store.setRow('pets', 'toto', {species: 'dog'});
store.setRow('pets', 'cujo', {next: 'toto'});
console.log(app.innerHTML);
// -> '<div>felix/cujo/toto/dog</div>'

This example creates a Provider context into which a default Relationships object is provided. The LinkedRowsView component within it then renders the local Row objects (with Ids for readability).

import {LinkedRowsView, Provider} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <LinkedRowsView
      relationshipId="petSequence"
      firstRowId="fido"
      debugIds={true}
    />
  </div>
);

const relationships = createRelationships(
  createStore().setTable('pets', {
    fido: {next: 'felix'},
    felix: {species: 'cat'},
  }),
).setRelationshipDefinition('petSequence', 'pets', 'pets', 'next');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<div>fido:{fido:{next:{felix}}felix:{species:{cat}}}</div>'

This example creates a Provider context into which a default Relationships object is provided. The LinkedRowsView component within it then renders the local Row objects with a custom Row component and a custom props callback.

import {LinkedRowsView, Provider, RowView} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const getBoldProp = (rowId) => ({bold: rowId == 'fido'});
const Pane = () => (
  <div>
    <LinkedRowsView
      relationshipId="petSequence"
      firstRowId="fido"
      rowComponent={FormattedRowView}
      getRowComponentProps={getBoldProp}
    />
  </div>
);
const FormattedRowView = ({store, tableId, rowId, bold}) => (
  <span>
    {bold ? <b>{rowId}</b> : rowId}
    {': '}
    <RowView store={store} tableId={tableId} rowId={rowId} />
  </span>
);

const relationships = createRelationships(
  createStore().setTable('pets', {
    fido: {next: 'felix'},
    felix: {species: 'cat'},
  }),
).setRelationshipDefinition('petSequence', 'pets', 'pets', 'next');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<div><span><b>fido</b>: felix</span><span>felix: cat</span></div>'
Since

v1.0.0

LocalRowsView

The LocalRowsView component renders the local Row objects for a given remote Row in a Relationship, and registers a listener so that any changes to that result will cause a re-render.

LocalRowsView(props: LocalRowsProps): ComponentReturnType
TypeDescription
propsLocalRowsProps

The props for this component.

returnsComponentReturnType

A rendering of the local Row objects, or nothing, if not present.

The component's props identify which local Rows to render based on Relationship Id, remote Row Id, and Relationships object (which is either the default context Relationships object, a named context Relationships object, or an explicit reference).

By default the local Rows are rendered with the RowView component, but you can override this behavior by providing a rowComponent prop, a custom component of your own that will render the Row based on RowProps. You can also pass additional props to your custom component with the getRowComponentProps callback prop.

This component uses the useLocalRowIds hook under the covers, which means that any changes to the local Row Ids in the Relationship will cause a re-render.

Examples

This example creates a Relationships object outside the application, which is used in the LocalRowsView component by reference. A change to the Row Ids re-renders the component.

import {createRelationships, createStore} from 'tinybase';
import {LocalRowsView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';

const store = createStore()
  .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
  .setTable('species', {wolf: {price: 10}, dog: {price: 5}});
const relationships = createRelationships(store).setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);
const App = () => (
  <div>
    <LocalRowsView
      relationshipId="petSpecies"
      remoteRowId="dog"
      relationships={relationships}
      separator="/"
    />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>dog/dog</div>'

store.setRow('pets', 'toto', {species: 'dog'});
console.log(app.innerHTML);
// -> '<div>dog/dog/dog</div>'

This example creates a Provider context into which a default Relationships object is provided. The LocalRowsView component within it then renders the local Row objects (with Ids for readability).

import {LocalRowsView, Provider} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <LocalRowsView
      relationshipId="petSpecies"
      remoteRowId="dog"
      debugIds={true}
    />
  </div>
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<div>dog:{fido:{species:{dog}}cujo:{species:{dog}}}</div>'

This example creates a Provider context into which a default Relationships object is provided. The LocalRowsView component within it then renders the local Row objects with a custom Row component and a custom props callback.

import {LocalRowsView, Provider, RowView} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const getBoldProp = (rowId) => ({bold: rowId == 'fido'});
const Pane = () => (
  <div>
    <LocalRowsView
      relationshipId="petSpecies"
      remoteRowId="dog"
      rowComponent={FormattedRowView}
      getRowComponentProps={getBoldProp}
    />
  </div>
);
const FormattedRowView = ({store, tableId, rowId, bold}) => (
  <span>
    {bold ? <b>{rowId}</b> : rowId}
    {': '}
    <RowView store={store} tableId={tableId} rowId={rowId} />
  </span>
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<div><span><b>fido</b>: dog</span><span>cujo: dog</span></div>'
Since

v1.0.0

RemoteRowView

The RemoteRowView component renders the remote Row Id for a given local Row in a Relationship, and registers a listener so that any changes to that result will cause a re-render.

RemoteRowView(props: RemoteRowProps): ComponentReturnType
TypeDescription
propsRemoteRowProps

The props for this component.

returnsComponentReturnType

A rendering of the remote Row, or nothing, if not present.

The component's props identify which remote Row to render based on Relationship Id, local Row Id, and Relationships object (which is either the default context Relationships object, a named context Relationships object, or an explicit reference).

By default the remote Row is rendered with the RowView component, but you can override this behavior by providing a rowComponent prop, a custom component of your own that will render the Row based on RowProps. You can also pass additional props to your custom component with the getRowComponentProps callback prop.

This component uses the useRemoteRowId hook under the covers, which means that any changes to the remote Row Id in the Relationship will cause a re-render.

Examples

This example creates a Relationships object outside the application, which is used in the RemoteRowView component by reference. A change to the Row Ids re-renders the component.

import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {RemoteRowView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';

const store = createStore()
  .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
  .setTable('species', {wolf: {price: 10}, dog: {price: 5}});
const relationships = createRelationships(store).setRelationshipDefinition(
  'petSpecies',
  'pets',
  'species',
  'species',
);
const App = () => (
  <div>
    <RemoteRowView
      relationshipId="petSpecies"
      localRowId="cujo"
      relationships={relationships}
    />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>5</div>'

store.setCell('pets', 'cujo', 'species', 'wolf');
console.log(app.innerHTML);
// -> '<div>10</div>'

This example creates a Provider context into which a default Relationships object is provided. The RemoteRowView component within it then renders the remote Row (with Ids for readability).

import {Provider, RemoteRowView} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <RemoteRowView
      relationshipId="petSpecies"
      localRowId="cujo"
      debugIds={true}
    />
  </div>
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<div>cujo:{dog:{price:{5}}}</div>'

This example creates a Provider context into which a default Relationships object is provided. The RemoteRowView component within it then renders the remote Row with a custom Row component and a custom props callback.

import {Provider, RemoteRowView, RowView} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const getBoldProp = (rowId) => ({bold: rowId == 'dog'});
const Pane = () => (
  <div>
    <RemoteRowView
      relationshipId="petSpecies"
      localRowId="cujo"
      rowComponent={FormattedRowView}
      getRowComponentProps={getBoldProp}
    />
  </div>
);
const FormattedRowView = ({store, tableId, rowId, bold}) => (
  <span>
    {bold ? <b>{rowId}</b> : rowId}
    {': '}
    <RowView store={store} tableId={tableId} rowId={rowId} />
  </span>
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// -> '<div><span><b>dog</b>: 5</span></div>'
Since

v1.0.0

Store components

This is the collection of store components within the ui-react module. There are 7 store components in total.

TablesView

The TablesView component renders the tabular contents of a Store, and registers a listener so that any changes to that result will cause a re-render.

TablesView(props: TablesProps): ComponentReturnType
TypeDescription
propsTablesProps

The props for this component.

returnsComponentReturnType

A rendering of the Store, or nothing, if not present.

The component's props can identify which Store to render - either the default context Store, a named context Store, or an explicit reference.

This component renders a Store by iterating over its Table objects. By default these are in turn rendered with the TableView component, but you can override this behavior by providing a tableComponent prop, a custom component of your own that will render a Table based on TableProps. You can also pass additional props to your custom component with the getTableComponentProps callback prop.

This component uses the useTableIds hook under the covers, which means that any changes to the structure of the Store will cause a re-render.

Examples

This example creates a Store outside the application, which is used in the TablesView component by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {TablesView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const App = () => (
  <div>
    <TablesView store={store} separator="/" />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>dog</div>'

store.setTable('species', {dog: {price: 5}});
console.log(app.innerHTML);
// -> '<div>dog/5</div>'

This example creates a Provider context into which a default Store is provided. The TablesView component within it then renders the Store (with Ids for readability).

import {Provider, TablesView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <TablesView debugIds={true} />
  </div>
);

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div>pets:{fido:{species:{dog}}}species:{dog:{price:{5}}}</div>'

This example creates a Provider context into which a default Store is provided. The TablesView component within it then renders the Store with a custom Table component and a custom props callback.

import {Provider, TableView, TablesView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const getBoldProp = (tableId) => ({bold: tableId == 'pets'});
const Pane = () => (
  <div>
    <TablesView
      tableComponent={FormattedTableView}
      getTableComponentProps={getBoldProp}
    />
  </div>
);
const FormattedTableView = ({tableId, bold}) => (
  <span>
    {bold ? <b>{tableId}</b> : tableId}
    {': '}
    <TableView tableId={tableId} />
  </span>
);

const store = createStore().setTables({
  pets: {fido: {species: 'dog'}},
  species: {dog: {price: 5}},
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div><span><b>pets</b>: dog</span><span>species: 5</span></div>'
Since

v1.0.0

TableView

The TableView component renders the contents of a single Table in a Store, and registers a listener so that any changes to that result will cause a re-render.

TableView(props: TableProps): ComponentReturnType
TypeDescription
propsTableProps

The props for this component.

returnsComponentReturnType

A rendering of the Table, or nothing, if not present.

The component's props identify which Table to render based on Table Id, and Store (which is either the default context Store, a named context Store, or by explicit reference).

This component renders a Table by iterating over its Row objects. By default these are in turn rendered with the RowView component, but you can override this behavior by providing a rowComponent prop, a custom component of your own that will render a Row based on RowProps. You can also pass additional props to your custom component with the getRowComponentProps callback prop.

You can create your own TableView-like component to customize the way that a Table is rendered: see the TablesView component for more details.

This component uses the useRowIds hook under the covers, which means that any changes to the structure of the Table will cause a re-render.

Since v4.1.0, you can use the customCellIds prop if you want to render a prescribed set of the Table's Cells in a given order for each Row.

Examples

This example creates a Store outside the application, which is used in the TableView component by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {TableView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTable('pets', {fido: {species: 'dog'}});
const App = () => (
  <div>
    <TableView tableId="pets" store={store} separator="/" />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>dog</div>'

store.setRow('pets', 'felix', {species: 'cat'});
console.log(app.innerHTML);
// -> '<div>dog/cat</div>'

This example creates a Provider context into which a default Store is provided. The TableView component within it then renders the Table for a custom set of Cell Ids (and rendered with Ids for readability).

import {Provider, TableView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const customCellIds = ['species'];
const Pane = () => (
  <div>
    <TableView
      tableId="pets"
      customCellIds={customCellIds}
      debugIds={true}
    />
  </div>
);

const store = createStore().setTable('pets', {
  fido: {color: 'black', species: 'dog'},
  felix: {color: 'brown', species: 'cat'},
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div>pets:{fido:{species:{dog}}felix:{species:{cat}}}</div>'

This example creates a Provider context into which a default Store is provided. The TableView component within it then renders the Table with a custom Row component and a custom props callback.

import {Provider, RowView, TableView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const getBoldProp = (rowId) => ({bold: rowId == 'fido'});
const Pane = () => (
  <div>
    <TableView
      tableId="pets"
      rowComponent={FormattedRowView}
      getRowComponentProps={getBoldProp}
    />
  </div>
);
const FormattedRowView = ({tableId, rowId, bold}) => (
  <span>
    {bold ? <b>{rowId}</b> : rowId}
    {': '}
    <RowView tableId={tableId} rowId={rowId} />
  </span>
);

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div><span><b>fido</b>: dog</span><span>felix: cat</span></div>'
Since

v1.0.0

SortedTableView

The SortedTableView component renders the contents of a single sorted Table in a Store, and registers a listener so that any changes to that result will cause a re-render.

SortedTableView(props: SortedTableProps): ComponentReturnType
TypeDescription
propsSortedTableProps

The props for this component.

returnsComponentReturnType

A rendering of the Table, or nothing, if not present.

The component's props identify which Table to render based on Table Id, and Store (which is either the default context Store, a named context Store, or by explicit reference). It also takes a Cell Id to sort by and a boolean to indicate that the sorting should be in descending order. The offset and limit props are used to paginate results, but default to 0 and undefined to return all available Row Ids if not specified.

This component renders a Table by iterating over its Row objects, in the order dictated by the sort parameters. By default these are in turn rendered with the RowView component, but you can override this behavior by providing a rowComponent prop, a custom component of your own that will render a Row based on RowProps. You can also pass additional props to your custom component with the getRowComponentProps callback prop.

This component uses the useSortedRowIds hook under the covers, which means that any changes to the structure or sorting of the Table will cause a re-render.

Since v4.1.0, you can use the customCellIds prop if you want to render a prescribed set of the Table's Cells in a given order for each Row.

Examples

This example creates a Store outside the application, which is used in the SortedTableView component by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {SortedTableView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
const App = () => (
  <div>
    <SortedTableView
      tableId="pets"
      cellId="species"
      store={store}
      separator="/"
    />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>cat/dog</div>'

store.setRow('pets', 'cujo', {species: 'wolf'});
console.log(app.innerHTML);
// -> '<div>cat/dog/wolf</div>'

This example creates a Provider context into which a default Store is provided. The SortedTableView component within it then renders the Table for a custom set of Cell Ids (and rendered with Ids for readability).

import {Provider, SortedTableView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const customCellIds = ['species'];
const Pane = () => (
  <div>
    <SortedTableView
      tableId="pets"
      cellId="species"
      customCellIds={customCellIds}
      debugIds={true}
    />
  </div>
);

const store = createStore().setTables({
  pets: {
    fido: {color: 'black', species: 'dog'},
    felix: {color: 'brown', species: 'cat'},
  },
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div>pets:{felix:{species:{cat}}fido:{species:{dog}}}</div>'

This example creates a Provider context into which a default Store is provided. The SortedTableView component within it then renders the Table with a custom Row component and a custom props callback.

import {Provider, RowView, SortedTableView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const getBoldProp = (rowId) => ({bold: rowId == 'fido'});
const Pane = () => (
  <div>
    <SortedTableView
      tableId="pets"
      cellId="species"
      rowComponent={FormattedRowView}
      getRowComponentProps={getBoldProp}
    />
  </div>
);
const FormattedRowView = ({tableId, rowId, bold}) => (
  <span>
    {bold ? <b>{rowId}</b> : rowId}
    {': '}
    <RowView tableId={tableId} rowId={rowId} />
  </span>
);

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div><span>felix: cat</span><span><b>fido</b>: dog</span></div>'
Since

v2.0.0

RowView

The RowView component renders the contents of a single Row in a given Table, and registers a listener so that any changes to that result will cause a re-render.

RowView(props: RowProps): ComponentReturnType
TypeDescription
propsRowProps

The props for this component.

returnsComponentReturnType

A rendering of the Row, or nothing, if not present.

The component's props identify which Row to render based on Table Id, Row Id, and Store (which is either the default context Store, a named context Store, or an explicit reference).

This component renders a Row by iterating over its Cell values. By default these are in turn rendered with the CellView component, but you can override this behavior by providing a cellComponent prop, a custom component of your own that will render a Cell based on CellProps. You can also pass additional props to your custom component with the getCellComponentProps callback prop.

You can create your own RowView-like component to customize the way that a Row is rendered: see the TableView component for more details.

Since v4.1.0, you can use the customCellIds prop if you want to render a prescribed set of the Row's Cells in a given order. Otherwise, this component uses the useCellIds hook under the covers, which means that any changes to the structure of the Row will cause a re-render.

Examples

This example creates a Store outside the application, which is used in the RowView component by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {RowView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setRow('pets', 'fido', {species: 'dog'});
const App = () => (
  <div>
    <RowView tableId="pets" rowId="fido" store={store} separator="/" />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>dog</div>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<div>dog/walnut</div>'

This example creates a Provider context into which a default Store is provided. The RowView component within it then renders the Row for a custom set of Cell Ids (and rendered with Ids for readability).

import {Provider, RowView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const customCellIds = ['color', 'species'];
const Pane = () => (
  <div>
    <RowView
      tableId="pets"
      rowId="fido"
      customCellIds={customCellIds}
      debugIds={true}
    />
  </div>
);

const store = createStore().setRow('pets', 'fido', {
  species: 'dog',
  color: 'walnut',
  legs: 4,
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div>fido:{color:{walnut}species:{dog}}</div>'

This example creates a Provider context into which a default Store is provided. The RowView component within it then renders the Row with a custom Cell component and a custom props callback.

import {CellView, Provider, RowView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const getBoldProp = (cellId) => ({bold: cellId == 'species'});
const Pane = () => (
  <div>
    <RowView
      tableId="pets"
      rowId="fido"
      cellComponent={FormattedCellView}
      getCellComponentProps={getBoldProp}
    />
  </div>
);
const FormattedCellView = ({tableId, rowId, cellId, bold}) => (
  <span>
    {bold ? <b>{cellId}</b> : cellId}
    {': '}
    <CellView tableId={tableId} rowId={rowId} cellId={cellId} />
  </span>
);

const store = createStore().setRow('pets', 'fido', {
  species: 'dog',
  color: 'walnut',
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div><span><b>species</b>: dog</span><span>color: walnut</span></div>'
Since

v1.0.0

CellView

The CellView component renders the value of a single Cell in a given Row, in a given Table, and registers a listener so that any changes to that result will cause a re-render.

CellView(props: CellProps): ComponentReturnType
TypeDescription
propsCellProps

The props for this component.

returnsComponentReturnType

A rendering of the Cell, or nothing, if not present.

The component's props identify which Cell to render based on Table Id, Row Id, Cell Id, and Store (which is either the default context Store, a named context Store, or an explicit reference).

A Cell contains a string, number, or boolean, so the value is rendered directly without further decoration. You can create your own CellView-like component to customize the way that a Cell is rendered: see the RowView component for more details.

This component uses the useCell hook under the covers, which means that any changes to the specified Cell will cause a re-render.

Examples

This example creates a Store outside the application, which is used in the CellView component by reference. A change to the data in the Store re-renders the component.

import {CellView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const App = () => (
  <span>
    <CellView tableId="pets" rowId="fido" cellId="color" store={store} />
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>brown</span>'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> '<span>walnut</span>'

This example creates a Provider context into which a default Store is provided. The CellView component within it then renders the Cell (with its Id for readability).

import {CellView, Provider} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    <CellView tableId="pets" rowId="fido" cellId="color" debugIds={true} />
  </span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>color:{brown}</span>'

This example creates a Provider context into which a default Store is provided. The CellView component within it then attempts to render a non-existent Cell.

import {CellView, Provider} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    <CellView tableId="pets" rowId="fido" cellId="height" />
  </span>
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span></span>'
Since

v1.0.0

ValuesView

The ValuesView component renders the keyed value contents of a Store, and registers a listener so that any changes to that result will cause a re-render.

ValuesView(props: ValuesProps): ComponentReturnType
TypeDescription
propsValuesProps

The props for this component.

returnsComponentReturnType

A rendering of the Values, or nothing, if not present.

The component's props can identify which Store to render - either the default context Store, a named context Store, or an explicit reference.

This component renders a Store by iterating over its Value objects. By default these are in turn rendered with the ValueView component, but you can override this behavior by providing a valueComponent prop, a custom component of your own that will render a Value based on ValueProps. You can also pass additional props to your custom component with the getValueComponentProps callback prop.

This component uses the useValueIds hook under the covers, which means that any changes to the Values in the Store will cause a re-render.

This component uses the useValueIds hook under the covers, which means that any changes to the Store's Values will cause a re-render.

Examples

This example creates a Store outside the application, which is used in the ValuesView component by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {ValuesView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setValue('open', true);
const App = () => (
  <div>
    <ValuesView store={store} separator="/" />
  </div>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<div>true</div>'

store.setValue('employees', 3);
console.log(app.innerHTML);
// -> '<div>true/3</div>'

This example creates a Provider context into which a default Store is provided. The ValuesView component within it then renders the Values (with Ids for readability).

import {Provider, ValuesView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <div>
    <ValuesView debugIds={true} />
  </div>
);

const store = createStore().setValues({open: true, employees: 3});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div>open:{true}employees:{3}</div>'

This example creates a Provider context into which a default Store is provided. The ValuesView component within it then renders the Values with a custom Value component and a custom props callback.

import {Provider, ValueView, ValuesView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const getBoldProp = (valueId) => ({bold: valueId == 'open'});
const Pane = () => (
  <div>
    <ValuesView
      valueComponent={FormattedValueView}
      getValueComponentProps={getBoldProp}
    />
  </div>
);
const FormattedValueView = ({valueId, bold}) => (
  <span>
    {bold ? <b>{valueId}</b> : valueId}
    {': '}
    <ValueView valueId={valueId} />
  </span>
);

const store = createStore().setValues({open: true, employees: 3});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<div><span><b>open</b>: true</span><span>employees: 3</span></div>'
Since

v3.0.0

ValueView

The ValueView component renders the value of a single Value, and registers a listener so that any changes to that result will cause a re-render.

ValueView(props: ValueProps): ComponentReturnType
TypeDescription
propsValueProps

The props for this component.

returnsComponentReturnType

A rendering of the Value, or nothing, if not present.

The component's props identify which Value to render based on Value Id and Store (which is either the default context Store, a named context Store, or an explicit reference).

A Value contains a string, number, or boolean, so the value is rendered directly without further decoration. You can create your own ValueView-like component to customize the way that a Value is rendered: see the ValuesView component for more details.

This component uses the useValue hook under the covers, which means that any changes to the specified Value will cause a re-render.

Examples

This example creates a Store outside the application, which is used in the ValueView component by reference. A change to the data in the Store re-renders the component.

import React from 'react';
import {ValueView} from 'tinybase/ui-react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const store = createStore().setValue('open', true);
const App = () => (
  <span>
    <ValueView valueId="open" store={store} />
  </span>
);

const app = document.createElement('div');
createRoot(app).render(<App />);
console.log(app.innerHTML);
// -> '<span>true</span>'

store.setValue('open', false);
console.log(app.innerHTML);
// -> '<span>false</span>'

This example creates a Provider context into which a default Store is provided. The ValueView component within it then renders the Value (with its Id for readability).

import {Provider, ValueView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    <ValueView valueId="open" debugIds={true} />
  </span>
);

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span>open:{true}</span>'

This example creates a Provider context into which a default Store is provided. The ValueView component within it then attempts to render a non-existent Value.

import {Provider, ValueView} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <span>
    <ValueView valueId="website" />
  </span>
);

const store = createStore().setValue('open', true);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// -> '<span></span>'
Since

v3.0.0

Other functions

This is the collection of other functions within the ui-react module. There are 6 other functions in total.

useProvideCheckpoints
useProvideCheckpoints(
  checkpointsId: string,
  checkpoints: Checkpoints,
): void
TypeDescription
checkpointsIdstring
checkpointsCheckpoints
returnsvoid

This has no return value.

useProvideIndexes
useProvideIndexes(
  indexesId: string,
  indexes: Indexes,
): void
TypeDescription
indexesIdstring
indexesIndexes
returnsvoid

This has no return value.

useProvidePersister
useProvidePersister(
  persisterId: string,
  persister: undefined | AnyPersister,
): void
TypeDescription
persisterIdstring
persisterundefined | AnyPersister
returnsvoid

This has no return value.

useProvideQueries
useProvideQueries(
  queriesId: string,
  queries: Queries,
): void
TypeDescription
queriesIdstring
queriesQueries
returnsvoid

This has no return value.

useProvideRelationships
useProvideRelationships(
  relationshipsId: string,
  relationships: Relationships,
): void
TypeDescription
relationshipsIdstring
relationshipsRelationships
returnsvoid

This has no return value.

useProvideSynchronizer
useProvideSynchronizer(
  synchronizerId: string,
  synchronizer: undefined | Synchronizer,
): void
TypeDescription
synchronizerIdstring
synchronizerundefined | Synchronizer
returnsvoid

This has no return value.

Type Aliases

These are the type aliases within the ui-react module.

Checkpoints type aliases

This is the collection of checkpoints type aliases within the ui-react module. There is only one type alias, UndoOrRedoInformation.

UndoOrRedoInformation

The UndoOrRedoInformation type is an array that you can fetch from a Checkpoints object to that indicates if and how you can move the state of the underlying Store forward or backward.

[boolean, Callback, Id | undefined, string]

This type is useful if you are building undo or redo buttons. See the useUndoInformation hook and the useRedoInformation hook for more details and examples.

Since

v1.0.0

Component type aliases

This is the collection of component type aliases within the ui-react module. There is only one type alias, ComponentReturnType.

ComponentReturnType

ComponentReturnType is a simple alias for what a React component can return: either a ReactElement, or null for an empty component.

ReactElement<any, any> | null
Since

v1.0.0

Identity type aliases

This is the collection of identity type aliases within the ui-react module. There are 9 identity type aliases in total.

StoreOrStoreId

The StoreOrStoreId type is used when you need to refer to a Store in a React hook or component.

Store | Id

In some simple cases you will already have a direct reference to the Store.

This module also includes a Provider component that can be used to wrap multiple Store objects into a context that can be used throughout the app. In this case you will want to refer to a Store by its Id in that context.

Many hooks and components in this ui-react module take this type as a parameter or a prop, allowing you to pass in either the Store or its Id.

Since

v1.0.0

CheckpointsOrCheckpointsId

The CheckpointsOrCheckpointsId type is used when you need to refer to a Checkpoints object in a React hook or component.

Checkpoints | Id

In some simple cases you will already have a direct reference to the Checkpoints object.

This module also includes a Provider component that can be used to wrap multiple Checkpoints objects into a context that can be used throughout the app. In this case you will want to refer to a Checkpoints object by its Id in that context.

Many hooks and components in this ui-react module take this type as a parameter or a prop, allowing you to pass in either the Checkpoints object or its Id.

Since

v1.0.0

GetId

The GetId type describes a function which, when passed a parameter, will return an Id.

(
  parameter: Parameter,
  store: Store,
): Id
TypeDescription
parameterParameter
storeStore
returnsId

This type is used in hooks that create callbacks - like the useSetTableCallback hook or useSetRowCallback hook - so that the Id arguments of the object to set can also be dependent on the event or parameter provided (as well as the object itself being set).

Since

v1.0.0

IndexesOrIndexesId

The IndexesOrIndexesId type is used when you need to refer to an Indexes object in a React hook or component.

Indexes | Id

In some simple cases you will already have a direct reference to the Indexes object.

This module also includes a Provider component that can be used to wrap multiple Indexes objects into a context that can be used throughout the app. In this case you will want to refer to an Indexes object by its Id in that context.

Many hooks and components in this ui-react module take this type as a parameter or a prop, allowing you to pass in either the Indexes object or its Id.

Since

v1.0.0

MetricsOrMetricsId

The MetricsOrMetricsId type is used when you need to refer to a Metrics object in a React hook or component.

Metrics | Id

In some simple cases you will already have a direct reference to the Metrics object.

This module also includes a Provider component that can be used to wrap multiple Metrics objects into a context that can be used throughout the app. In this case you will want to refer to a Metrics object by its Id in that context.

Many hooks and components in this ui-react module take this type as a parameter or a prop, allowing you to pass in either the Metrics object or its Id.

Since

v1.0.0

PersisterOrPersisterId

The PersisterOrPersisterId type is used when you need to refer to a Persister object in a React hook or component.

AnyPersister | Id

In some simple cases you will already have a direct reference to the Persister object.

This module also includes a Provider component that can be used to wrap multiple Persister objects into a context that can be used throughout the app. In this case you will want to refer to a Persister object by its Id in that context.

Many hooks and components in this ui-react module take this type as a parameter or a prop, allowing you to pass in either the Persister or its Id.

Since

v5.3.0

QueriesOrQueriesId

The QueriesOrQueriesId type is used when you need to refer to a Queries object in a React hook or component.

Queries | Id

In some simple cases you will already have a direct reference to the Queries object.

This module also includes a Provider component that can be used to wrap multiple Queries objects into a context that can be used throughout the app. In this case you will want to refer to a Queries object by its Id in that context.

Many hooks and components in this ui-react module take this type as a parameter or a prop, allowing you to pass in either the Queries object or its Id.

Since

v2.0.0

RelationshipsOrRelationshipsId

The RelationshipsOrRelationshipsId type is used when you need to refer to a Relationships object in a React hook or component.

Relationships | Id

In some simple cases you will already have a direct reference to the Relationships object.

This module also includes a Provider component that can be used to wrap multiple Relationships objects into a context that can be used throughout the app. In this case you will want to refer to a Relationships object by its Id in that context.

Many hooks and components in this ui-react module take this type as a parameter or a prop, allowing you to pass in either the Relationships object or its Id.

Since

v1.0.0

SynchronizerOrSynchronizerId

The SynchronizerOrSynchronizerId type is used when you need to refer to a Synchronizer object in a React hook or component.

Synchronizer | Id

In some simple cases you will already have a direct reference to the Synchronizer object.

This module also includes a Provider component that can be used to wrap multiple Synchronizer objects into a context that can be used throughout the app. In this case you will want to refer to a Synchronizer object by its Id in that context.

Many hooks and components in this ui-react module take this type as a parameter or a prop, allowing you to pass in either the Synchronizer or its Id.

Since

v5.3.0

Props type aliases

This is the collection of props type aliases within the ui-react module. There are 23 props type aliases in total.

TablesProps

TablesProps props are used for components that refer to all the Tables in a Store, such as the TablesView component.

{
  store?: StoreOrStoreId;
  tableComponent?: ComponentType<TableProps>;
  getTableComponentProps?: (tableId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

tableComponent?ComponentType<TableProps>

A component for rendering each Table in the Store (to override the default TableView component).

getTableComponentProps?(tableId: Id) => ExtraProps

A custom function for generating extra props for each Table component based on its Id.

separator?ReactElement | string

A component or string to separate each Table component.

debugIds?boolean

Whether the component should also render the Ids of each Table, and its descendent objects, to assist with debugging.

Since

v1.0.0

TableProps

TableProps props are used for components that refer to a single Table in a Store, such as the TableView component.

{
  tableId: Id;
  store?: StoreOrStoreId;
  rowComponent?: ComponentType<RowProps>;
  getRowComponentProps?: (rowId: Id) => ExtraProps;
  customCellIds?: Ids;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
tableIdId

The Id of the Table in the Store to be rendered.

store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

rowComponent?ComponentType<RowProps>

A custom component for rendering each Row in the Table (to override the default RowView component).

getRowComponentProps?(rowId: Id) => ExtraProps

A function for generating extra props for each custom Row component based on its Id.

customCellIds?Ids

An optional list of Cell Ids to use for rendering a prescribed set of the Table's Cells in a given order.

separator?ReactElement | string

A component or string to separate each Row component.

debugIds?boolean

Whether the component should also render the Id of the Table, and its descendent objects, to assist with debugging.

Since

v1.0.0

SortedTableProps

SortedTableProps props are used for components that refer to a single sorted Table in a Store, such as the SortedTableView component.

{
  tableId: Id;
  cellId?: Id;
  descending?: boolean;
  offset?: number;
  limit?: number;
  store?: StoreOrStoreId;
  rowComponent?: ComponentType<RowProps>;
  getRowComponentProps?: (rowId: Id) => ExtraProps;
  customCellIds?: Ids;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
tableIdId

The Id of the Table in the Store to be rendered.

cellId?Id

The Id of the Cell whose values are used for the sorting. If omitted, the view will sort the Row Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of Row Ids to skip for pagination purposes.

limit?number

The maximum number of Row Ids to return.

store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

rowComponent?ComponentType<RowProps>

A custom component for rendering each Row in the Table (to override the default RowView component).

getRowComponentProps?(rowId: Id) => ExtraProps

A function for generating extra props for each custom Row component based on its Id.

customCellIds?Ids

An optional list of Cell Ids to use for rendering a prescribed set of the sorted Table's Cells in a given order.

separator?ReactElement | string

A component or string to separate each Row component.

debugIds?boolean

Whether the component should also render the Id of the Table, and its descendent objects, to assist with debugging.

Since

v2.0.0

RowProps

RowProps props are used for components that refer to a single Row in a Table, such as the RowView component.

{
  tableId: Id;
  rowId: Id;
  store?: StoreOrStoreId;
  cellComponent?: ComponentType<CellProps>;
  getCellComponentProps?: (cellId: Id) => ExtraProps;
  customCellIds?: Ids;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
tableIdId

The Id of the Table in the Store.

rowIdId

The Id of the Row in the Table to be rendered.

store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

cellComponent?ComponentType<CellProps>

A custom component for rendering each Cell in the Row (to override the default CellView component).

getCellComponentProps?(cellId: Id) => ExtraProps

A function for generating extra props for each custom Cell component based on its Id.

customCellIds?Ids

An optional list of Cell Ids to use for rendering a prescribed set of the Row's Cells in a given order.

separator?ReactElement | string

A component or string to separate each Cell component.

debugIds?boolean

Whether the component should also render the Id of the Row, and its descendent objects, to assist with debugging.

Since

v1.0.0

CellProps

CellProps props are used for components that refer to a single Cell in a Row, such as the CellView component.

{
  tableId: Id;
  rowId: Id;
  cellId: Id;
  store?: StoreOrStoreId;
  debugIds?: boolean;
}
TypeDescription
tableIdId

The Id of the Table in the Store.

rowIdId

The Id of the Row in the Table.

cellIdId

The Id of the Cell in the Row to be rendered.

store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

debugIds?boolean

Whether the component should also render the Id of the Cell to assist with debugging.

Since

v1.0.0

MetricProps

MetricProps props are used for components that refer to a single Metric in a Metrics object, such as the MetricView component.

{
  metricId: Id;
  metrics?: MetricsOrMetricsId;
  debugIds?: boolean;
}
TypeDescription
metricIdId

The Id of the Metric in the Metrics object to be rendered.

metrics?MetricsOrMetricsId

The Metrics object to be accessed: omit for the default context Metrics object, provide an Id for a named context Metrics object, or provide an explicit reference.

debugIds?boolean

Whether the component should also render the Id of the Metric to assist with debugging.

Since

v1.0.0

IndexProps

IndexProps props are used for components that refer to a single Index in an Indexes object, such as the IndexView component.

{
  indexId: Id;
  indexes?: IndexesOrIndexesId;
  sliceComponent?: ComponentType<SliceProps>;
  getSliceComponentProps?: (sliceId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
indexIdId

The Id of the Index in the Indexes object to be rendered.

indexes?IndexesOrIndexesId

The Indexes object to be accessed: omit for the default context Indexes object, provide an Id for a named context Indexes object, or provide an explicit reference.

sliceComponent?ComponentType<SliceProps>

A component for rendering each Slice in the Index.

getSliceComponentProps?(sliceId: Id) => ExtraProps

A function for generating extra props for each Slice component based on its Id.

separator?ReactElement | string

A component or string to separate each Slice component.

debugIds?boolean

Whether the component should also render the Id of the Index, and its descendent objects, to assist with debugging.

Since

v1.0.0

SliceProps

SliceProps props are used for components that refer to a single Slice in an Index object, such as the SliceView component.

{
  indexId: Id;
  sliceId: Id;
  indexes?: IndexesOrIndexesId;
  rowComponent?: ComponentType<RowProps>;
  getRowComponentProps?: (rowId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
indexIdId

The Id of the Index in the Indexes object.

sliceIdId

The Id of the Slice in the Index to be rendered.

indexes?IndexesOrIndexesId

The Indexes object to be accessed: omit for the default context Indexes object, provide an Id for a named context Indexes object, or provide an explicit reference.

rowComponent?ComponentType<RowProps>

A component for rendering each Row in the Index.

getRowComponentProps?(rowId: Id) => ExtraProps

A function for generating extra props for each Row component based on its Id.

separator?ReactElement | string

A component or string to separate each Row component.

debugIds?boolean

Whether the component should also render the Id of the Slice, and its descendent objects, to assist with debugging.

Since

v1.0.0

LocalRowsProps

LocalRowsProps props are used for components that refer to a single Relationship in a Relationships object, and where you want to render local Rows based on a remote Row, such as the LocalRowsView component.

{
  relationshipId: Id;
  remoteRowId: Id;
  relationships?: RelationshipsOrRelationshipsId;
  rowComponent?: ComponentType<RowProps>;
  getRowComponentProps?: (rowId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
relationshipIdId

The Id of the Relationship in the Relationships object.

remoteRowIdId

The Id of the remote Row for which to render the local Rows.

relationships?RelationshipsOrRelationshipsId

The Relationships object to be accessed: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

rowComponent?ComponentType<RowProps>

A component for rendering each (remote, local, or linked) Row in the Relationship.

getRowComponentProps?(rowId: Id) => ExtraProps

A function for generating extra props for each Row component based on its Id.

separator?ReactElement | string

A component or string to separate each Row component.

debugIds?boolean

Whether the component should also render the Id of the Row in the Relationship, and its descendent objects, to assist with debugging.

Since

v1.0.0

RemoteRowProps

RemoteRowProps props are used for components that refer to a single Relationship in a Relationships object, and where you want to render a remote Row based on a local Row, such as in the RemoteRowView component.

{
  relationshipId: Id;
  localRowId: Id;
  relationships?: RelationshipsOrRelationshipsId;
  rowComponent?: ComponentType<RowProps>;
  getRowComponentProps?: (rowId: Id) => ExtraProps;
  debugIds?: boolean;
}
TypeDescription
relationshipIdId

The Id of the Relationship in the Relationships object.

localRowIdId

The Id of the local Row for which to render the remote Row.

relationships?RelationshipsOrRelationshipsId

The Relationships object to be accessed: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

rowComponent?ComponentType<RowProps>

A component for rendering each (remote, local, or linked) Row in the Relationship.

getRowComponentProps?(rowId: Id) => ExtraProps

A function for generating extra props for each Row component based on its Id.

debugIds?boolean

Whether the component should also render the Id of the Row in the Relationship, and its descendent objects, to assist with debugging.

Since

v1.0.0

LinkedRowsProps

LinkedRowsProps props are used for components that refer to a single Relationship in a Relationships object, and where you want to render a linked list of Rows starting from a first Row, such as the LinkedRowsView component.

{
  relationshipId: Id;
  firstRowId: Id;
  relationships?: RelationshipsOrRelationshipsId;
  rowComponent?: ComponentType<RowProps>;
  getRowComponentProps?: (rowId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
relationshipIdId

The Id of the Relationship in the Relationships object.

firstRowIdId

The Id of the first Row in the linked list Relationship.

relationships?RelationshipsOrRelationshipsId

The Relationships object to be accessed: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

rowComponent?ComponentType<RowProps>

A component for rendering each (remote, local, or linked) Row in the Relationship.

getRowComponentProps?(rowId: Id) => ExtraProps

A function for generating extra props for each Row component based on its Id.

separator?ReactElement | string

A component or string to separate each Row component.

debugIds?boolean

Whether the component should also render the Id of the Row in the Relationship, and its descendent objects, to assist with debugging.

Since

v1.0.0

ResultTableProps

ResultTableProps props are used for components that refer to a single query ResultTable, such as the ResultTableView component.

{
  queryId: Id;
  queries?: QueriesOrQueriesId;
  resultRowComponent?: ComponentType<ResultRowProps>;
  getResultRowComponentProps?: (rowId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
queryIdId

The Id of the query in the Queries object for which the ResultTable will be rendered.

queries?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

resultRowComponent?ComponentType<ResultRowProps>

A custom component for rendering each Row in the Table (to override the default ResultRowView component).

getResultRowComponentProps?(rowId: Id) => ExtraProps

A function for generating extra props for each custom Row component based on its Id.

separator?ReactElement | string

A component or string to separate each Row component.

debugIds?boolean

Whether the component should also render the Id of the query, and its descendent objects, to assist with debugging.

Since

v2.0.0

ResultSortedTableProps

ResultSortedTableProps props are used for components that refer to a single sorted query ResultTable, such as the ResultSortedTableView component.

{
  queryId: Id;
  cellId?: Id;
  descending?: boolean;
  offset?: number;
  limit?: number;
  queries?: QueriesOrQueriesId;
  resultRowComponent?: ComponentType<ResultRowProps>;
  getResultRowComponentProps?: (rowId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
queryIdId

The Id of the query in the Queries object for which the sorted ResultTable will be rendered.

cellId?Id

The Id of the Cell whose values are used for the sorting. If omitted, the view will sort the Row Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of Row Ids to skip for pagination purposes.

limit?number

The maximum number of Row Ids to return.

queries?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

resultRowComponent?ComponentType<ResultRowProps>

A custom component for rendering each Row in the Table (to override the default ResultRowView component).

getResultRowComponentProps?(rowId: Id) => ExtraProps

A function for generating extra props for each custom Row component based on its Id.

separator?ReactElement | string

A component or string to separate each Row component.

debugIds?boolean

Whether the component should also render the Id of the query, and its descendent objects, to assist with debugging.

Since

v2.0.0

ResultRowProps

ResultRowProps props are used for components that refer to a single Row in a query ResultTable, such as the ResultRowView component.

{
  queryId: Id;
  rowId: Id;
  queries?: QueriesOrQueriesId;
  resultCellComponent?: ComponentType<ResultCellProps>;
  getResultCellComponentProps?: (cellId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
queryIdId

The Id of the query in the Queries object for which the ResultTable will be rendered.

rowIdId

The Id of the Row in the ResultTable to be rendered.

queries?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

resultCellComponent?ComponentType<ResultCellProps>

A custom component for rendering each Cell in the Row (to override the default CellView component).

getResultCellComponentProps?(cellId: Id) => ExtraProps

A function for generating extra props for each custom Cell component based on its Id.

separator?ReactElement | string

A component or string to separate each Cell component.

debugIds?boolean

Whether the component should also render the Id of the Row, and its descendent objects, to assist with debugging.

Since

v2.0.0

ResultCellProps

ResultRowProps props are used for components that refer to a single Cell in a Row of a ResultTable, such as the ResultCellView component.

{
  queryId: Id;
  rowId: Id;
  cellId: Id;
  queries?: QueriesOrQueriesId;
  debugIds?: boolean;
}
TypeDescription
queryIdId

The Id of the query in the Queries object for which the ResultTable will be rendered.

rowIdId

The Id of the Row in the Table.

cellIdId

The Id of the Cell in the Row to be rendered.

queries?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

debugIds?boolean

Whether the component should also render the Id of the Cell to assist with debugging.

Since

v2.0.0

BackwardCheckpointsProps

BackwardCheckpointsProps props are used for components that refer to a list of previous checkpoints in a Checkpoints object, such as the BackwardCheckpointsView component.

{
  checkpoints?: CheckpointsOrCheckpointsId;
  checkpointComponent?: ComponentType<CheckpointProps>;
  getCheckpointComponentProps?: (checkpointId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
checkpoints?CheckpointsOrCheckpointsId

The Checkpoints object to be accessed: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

checkpointComponent?ComponentType<CheckpointProps>

A component for rendering each checkpoint in the Checkpoints object.

getCheckpointComponentProps?(checkpointId: Id) => ExtraProps

A function for generating extra props for each checkpoint component based on its Id.

separator?ReactElement | string

A component or string to separate each Checkpoint component.

debugIds?boolean

Whether the component should also render the Ids of the checkpoints to assist with debugging.

Since

v1.0.0

CurrentCheckpointProps

CurrentCheckpointsProps props are used for components that refer to the current checkpoints in a Checkpoints object, such as the BackwardCheckpointsView component.

{
  checkpoints?: CheckpointsOrCheckpointsId;
  checkpointComponent?: ComponentType<CheckpointProps>;
  getCheckpointComponentProps?: (checkpointId: Id) => ExtraProps;
  debugIds?: boolean;
}
TypeDescription
checkpoints?CheckpointsOrCheckpointsId

The Checkpoints object to be accessed: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

checkpointComponent?ComponentType<CheckpointProps>

A component for rendering each checkpoint in the Checkpoints object.

getCheckpointComponentProps?(checkpointId: Id) => ExtraProps

A function for generating extra props for each checkpoint component based on its Id.

debugIds?boolean

Whether the component should also render the Ids of the checkpoints to assist with debugging.

Since

v1.0.0

ForwardCheckpointsProps

ForwardCheckpointsProps props are used for components that refer to a list of future checkpoints in a Checkpoints object, such as the ForwardCheckpointsView component.

{
  checkpoints?: CheckpointsOrCheckpointsId;
  checkpointComponent?: ComponentType<CheckpointProps>;
  getCheckpointComponentProps?: (checkpointId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
checkpoints?CheckpointsOrCheckpointsId

The Checkpoints object to be accessed: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

checkpointComponent?ComponentType<CheckpointProps>

A component for rendering each checkpoint in the Checkpoints object.

getCheckpointComponentProps?(checkpointId: Id) => ExtraProps

A function for generating extra props for each checkpoint component based on its Id.

separator?ReactElement | string

A component or string to separate each Checkpoint component.

debugIds?boolean

Whether the component should also render the Ids of the checkpoints to assist with debugging.

Since

v1.0.0

ProviderProps

ProviderProps props are used with the Provider component, so that Store Metrics, Indexes, Relationships, Queries, and Checkpoints objects can be passed into the context of an application and used throughout.

{
  store?: Store;
  storesById?: {[storeId: Id]: Store};
  metrics?: Metrics;
  metricsById?: {[metricsId: Id]: Metrics};
  indexes?: Indexes;
  indexesById?: {[indexesId: Id]: Indexes};
  relationships?: Relationships;
  relationshipsById?: {[relationshipsId: Id]: Relationships};
  queries?: Queries;
  queriesById?: {[queriesId: Id]: Queries};
  checkpoints?: Checkpoints;
  checkpointsById?: {[checkpointsId: Id]: Checkpoints};
  persister?: AnyPersister;
  persistersById?: {[persisterId: Id]: AnyPersister};
  synchronizer?: Synchronizer;
  synchronizersById?: {[synchronizerId: Id]: Synchronizer};
}
TypeDescription
store?Store

A default single Store object that will be available within the Provider context.

storesById?{[storeId: Id]: Store}

An object containing multiple Store objects that will be available within the Provider context by their Id.

metrics?Metrics

A default single Metrics object that will be available within the Provider context.

metricsById?{[metricsId: Id]: Metrics}

An object containing multiple Metrics objects that will be available within the Provider context by their Id.

indexes?Indexes

A default single Indexes object that will be available within the Provider context.

indexesById?{[indexesId: Id]: Indexes}

An object containing multiple Indexes objects that will be available within the Provider context by their Id.

relationships?Relationships

A default single Relationships object that will be available within the Provider context.

relationshipsById?{[relationshipsId: Id]: Relationships}

An object containing multiple Relationships objects that will be available within the Provider context by their Id.

queries?Queries

A default single Queries object that will be available within the Provider context, since v2.0.

queriesById?{[queriesId: Id]: Queries}

An object containing multiple Queries objects that will be available within the Provider context by their Id, since v2.0.

checkpoints?Checkpoints

A default single Checkpoints object that will be available within the Provider context.

checkpointsById?{[checkpointsId: Id]: Checkpoints}

An object containing multiple Checkpoints objects that will be available within the Provider context by their Id.

persister?AnyPersister

A default single Persister object that will be available within the Provider context.

persistersById?{[persisterId: Id]: AnyPersister}

An object containing multiple Persister objects that will be available within the Provider context by their Id.

synchronizer?Synchronizer

A default single Synchronizer object that will be available within the Provider context.

synchronizersById?{[synchronizerId: Id]: Synchronizer}

An object containing multiple Synchronizer objects that will be available within the Provider context by their Id.

One of each type of object can be provided as a default within the context. Additionally, multiple of each type of object can be provided in an Id-keyed map to the ___ById props.

Since

v1.0.0

ExtraProps

The ExtraProps type represents a set of arbitrary additional props.

{[propName: string]: any}
Since

v1.0.0

ValuesProps

ValuesProps props are used for components that refer to all the Values in a Store, such as the ValuesView component.

{
  store?: StoreOrStoreId;
  valueComponent?: ComponentType<ValueProps>;
  getValueComponentProps?: (valueId: Id) => ExtraProps;
  separator?: ReactElement | string;
  debugIds?: boolean;
}
TypeDescription
store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

valueComponent?ComponentType<ValueProps>

A custom component for rendering each Value in the Store (to override the default ValueView component).

getValueComponentProps?(valueId: Id) => ExtraProps

A function for generating extra props for each custom Value component based on its Id.

separator?ReactElement | string

A component or string to separate each Value component.

debugIds?boolean

Whether the component should also render the Ids of each Value to assist with debugging.

Since

v3.0.0

ValueProps

ValueProps props are used for components that refer to a single Value in a Row, such as the ValueView component.

{
  valueId: Id;
  store?: StoreOrStoreId;
  debugIds?: boolean;
}
TypeDescription
valueIdId

The Id of the Value in the Row to be rendered.

store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

debugIds?boolean

Whether the component should also render the Id of the Value to assist with debugging.

Since

v3.0.0

CheckpointProps

CheckpointProps props are used for components that refer to a single checkpoint in a Checkpoints object, such as the CheckpointView component.

{
  checkpointId: Id;
  checkpoints?: CheckpointsOrCheckpointsId;
  debugIds?: boolean;
}
TypeDescription
checkpointIdId

The Id of the checkpoint in the Checkpoints object.

checkpoints?CheckpointsOrCheckpointsId

The Checkpoints object to be accessed: omit for the default context Checkpoints object, provide an Id for a named context Checkpoints object, or provide an explicit reference.

debugIds?boolean

Whether the component should also render the Id of the checkpoint to assist with debugging.

Since

v1.0.0

ui-react-dom

The ui-react-dom module of the TinyBase project provides components to make it easy to create web-based reactive apps with Store objects.

The components in this module use the react-dom module and so are not appropriate for environments like React Native (although those in the lower-level ui-react module are).

See also

UI Components demos

Since

v4.1.0

Functions

These are the functions within the ui-react-dom module.

Indexes components

This is the collection of indexes components within the ui-react-dom module. There is only one function, SliceInHtmlTable.

SliceInHtmlTable

The SliceInHtmlTable component renders the contents of a Slice as an HTML

<table> element, and registers a listener so that any changes to that result will cause a re-render.
SliceInHtmlTable(props: SliceInHtmlTableProps & HtmlTableProps): ComponentReturnType
TypeDescription
propsSliceInHtmlTableProps & HtmlTableProps

The props for this component.

returnsComponentReturnType

A rendering of the Slice in a <table> element.

See the <SliceInHtmlTable /> demo for this component in action.

The component's props identify which Slice to render based on Index Id, Slice Id, and Indexes object (which is either the default context Indexes object, a named context Indexes object, or an explicit reference).

This component renders a Slice by iterating over its Row objects. By default the Cells are in turn rendered with the CellView component, but you can override this behavior by providing a component for each Cell in the customCells prop. You can pass additional props to that custom component with the getComponentProps callback. See the CustomCell type for more details.

This component uses the useSliceRowIds hook under the covers, which means that any changes to the structure of the Slice will cause a re-render.

You can use the headerRow and idColumn props to control whether labels and Ids appear in a <th> element at the top of the table, and the start of each row.

Examples

This example creates a Provider context into which a default Indexes object is provided. The SliceInHtmlTable component within it then renders the Slice in a <table> element with a CSS class.

import {createIndexes, createStore} from 'tinybase';
import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {SliceInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <SliceInHtmlTable indexId="bySpecies" sliceId="dog" className="slice" />
);

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// ->
`
<table class="slice">
  <thead>
    <tr>
      <th>Id</th>
      <th>species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>fido</th>
      <td>dog</td>
    </tr>
    <tr>
      <th>cujo</th>
      <td>dog</td>
    </tr>
  </tbody>
</table>
`;

This example creates a Provider context into which a default Indexes object is provided. The SliceInHtmlTable component within it then renders the Slice with a custom component and a custom props callback for the species Cell. The header row at the top of the table and the Id column at the start of each row is removed.

import {CellView, Provider} from 'tinybase/ui-react';
import {createIndexes, createStore} from 'tinybase';
import React from 'react';
import {SliceInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';

const App = ({indexes}) => (
  <Provider indexes={indexes}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <SliceInHtmlTable
    indexId="bySpecies"
    sliceId="dog"
    customCells={customCells}
    headerRow={false}
    idColumn={false}
  />
);

const FormattedCellView = ({tableId, rowId, cellId, bold}) => (
  <>
    {bold ? <b>{rowId}</b> : rowId}:
    <CellView tableId={tableId} rowId={rowId} cellId={cellId} />
  </>
);
const customCells = {
  species: {
    component: FormattedCellView,
    getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
  },
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat'},
  cujo: {species: 'dog'},
});
const indexes = createIndexes(store);
indexes.setIndexDefinition('bySpecies', 'pets', 'species');

const app = document.createElement('div');
createRoot(app).render(<App indexes={indexes} />);
console.log(app.innerHTML);
// ->
`
<table>
  <tbody>
    <tr>
      <td><b>fido</b>:</td>
    </tr>
    <tr>
      <td>cujo:</td>
    </tr>
  </tbody>
</table>
`;
Since

v4.1.0

Queries components

This is the collection of queries components within the ui-react-dom module. There are only two queries components, ResultSortedTableInHtmlTable and ResultTableInHtmlTable.

ResultSortedTableInHtmlTable

The SortedTableInHtmlTable component renders the contents of a single query's sorted ResultTable in a Queries object as an HTML <table> element, and registers a listener so that any changes to that result will cause a re-render.

ResultSortedTableInHtmlTable(props: ResultSortedTableInHtmlTableProps & HtmlTableProps): ComponentReturnType
TypeDescription
propsResultSortedTableInHtmlTableProps & HtmlTableProps

The props for this component.

returnsComponentReturnType

A rendering of the ResultTable in a <table> element.

See the <ResultSortedTableInHtmlTable /> demo for this component in action.

The component's props identify which ResultTable to render based on query Id, and Queries object (which is either the default context Queries object, a named context Queries object, or by explicit reference). It also takes a Cell Id to sort by and a boolean to indicate that the sorting should be in descending order. The offset and limit props are used to paginate results, but default to 0 and undefined to return all available Row Ids if not specified.

This component renders a ResultTable by iterating over its Row objects, in the order dictated by the sort parameters. By default the Cells are in turn rendered with the CellView component, but you can override this behavior by providing a component for each Cell in the customCells prop. You can pass additional props to that custom component with the getComponentProps callback. See the ResultCustomCell type for more details.

This component uses the useSortedRowIds hook under the covers, which means that any changes to the structure or sorting of the ResultTable will cause a re-render.

You can use the headerRow and idColumn props to control whether the Ids appear in a <th> element at the top of the table, and the start of each row.

The sortOnClick prop makes the table's sorting interactive such that the user can click on a column heading to sort by that column. The style classes sorted and ascending (or descending) are added so that you can provide hints to the user how the sorting is being applied.

Provide a paginator component for the ResultTable with the paginator prop. Set to true to use the default SortedTablePaginator, or provide your own component that accepts SortedTablePaginatorProps.

Finally, the onChange prop lets you listen to a user's changes to the ResultTable's sorting or pagination.

Examples

This example creates a Provider context into which a default Queries object is provided. The ResultSortedTableInHtmlTable component within it then renders the ResultTable in a <table> element with a CSS class.

import {createQueries, createStore} from 'tinybase';
import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {ResultSortedTableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <ResultSortedTableInHtmlTable
    queryId="petColors"
    cellId="color"
    className="table"
  />
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// ->
`
<table class="table">
  <thead>
    <tr>
      <th>Id</th>
      <th class="sorted ascending">↑ color</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>felix</th>
      <td>black</td>
    </tr>
    <tr>
      <th>fido</th>
      <td>brown</td>
    </tr>
  </tbody>
</table>
`;

This example creates a Provider context into which a default Queries object is provided. The ResultSortedTableInHtmlTable component within it then renders the ResultTable with a custom component and a custom props callback for the color Cell. The header row at the top of the table and the Id column at the start of each row is removed.

import {Provider, ResultCellView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {ResultSortedTableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);

const Pane = () => (
  <ResultSortedTableInHtmlTable
    queryId="petColors"
    cellId="color"
    customCells={customCells}
    headerRow={false}
    idColumn={false}
  />
);

const FormattedResultCellView = ({queryId, rowId, cellId, bold}) => (
  <>
    {bold ? <b>{rowId}</b> : rowId}:
    <ResultCellView queryId={queryId} rowId={rowId} cellId={cellId} />
  </>
);
const customCells = {
  color: {
    component: FormattedResultCellView,
    getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
  },
};

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// ->
`
<table>
  <tbody>
    <tr>
      <td>felix:black</td>
    </tr>
    <tr>
      <td><b>fido</b>:brown</td>
    </tr>
  </tbody>
</table>
`;
Since

v4.1.0

ResultTableInHtmlTable

The ResultTableInHtmlTable component renders the contents of a single query's ResultTable in a Queries object as an HTML <table> element, and registers a listener so that any changes to that result will cause a re-render.

ResultTableInHtmlTable(props: ResultTableInHtmlTableProps & HtmlTableProps): ComponentReturnType
TypeDescription
propsResultTableInHtmlTableProps & HtmlTableProps

The props for this component.

returnsComponentReturnType

A rendering of the ResultTable in a <table> element.

See the <ResultTableInHtmlTable /> demo for this component in action.

The component's props identify which ResultTable to render based on query Id, and Queries object (which is either the default context Queries object, a named context Queries object, or by explicit reference).

This component renders a ResultTable by iterating over its Row objects. By default the Cells are in turn rendered with the CellView component, but you can override this behavior by providing a component for each Cell in the customCells prop. You can pass additional props to that custom component with the getComponentProps callback. See the ResultCustomCell type for more details.

This component uses the useRowIds hook under the covers, which means that any changes to the structure of the Table will cause a re-render.

You can use the headerRow and idColumn props to control whether the Ids appear in a <th> element at the top of the table, and the start of each row.

Examples

This example creates a Provider context into which a default Queries object is provided. The ResultTableInHtmlTable component within it then renders the ResultTable in a <table> element with a CSS class.

import {createQueries, createStore} from 'tinybase';
import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {ResultTableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <ResultTableInHtmlTable queryId="petColors" className="table" />
);

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// ->
`
<table class="table">
  <thead>
    <tr>
      <th>Id</th>
      <th>color</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>fido</th>
      <td>brown</td>
    </tr>
    <tr>
      <th>felix</th>
      <td>black</td>
    </tr>
  </tbody>
</table>
`;

This example creates a Provider context into which a default Queries object is provided. The ResultTableInHtmlTable component within it then renders the ResultTable with a custom component and a custom props callback for the color Cell. The header row at the top of the table and the Id column at the start of each row is removed.

import {Provider, ResultCellView} from 'tinybase/ui-react';
import {createQueries, createStore} from 'tinybase';
import React from 'react';
import {ResultTableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';

const App = ({queries}) => (
  <Provider queries={queries}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <ResultTableInHtmlTable
    queryId="petColors"
    customCells={customCells}
    headerRow={false}
    idColumn={false}
  />
);

const FormattedResultCellView = ({queryId, rowId, cellId, bold}) => (
  <>
    {bold ? <b>{rowId}</b> : rowId}:
    <ResultCellView queryId={queryId} rowId={rowId} cellId={cellId} />
  </>
);
const customCells = {
  color: {
    component: FormattedResultCellView,
    getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
  },
};

const queries = createQueries(
  createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  }),
).setQueryDefinition('petColors', 'pets', ({select}) => select('color'));
const app = document.createElement('div');
createRoot(app).render(<App queries={queries} />);
console.log(app.innerHTML);
// ->
`
<table>
  <tbody>
    <tr>
      <td><b>fido</b>:brown</td>
    </tr>
    <tr>
      <td>felix:black</td>
    </tr>
  </tbody>
</table>
`;
Since

v4.1.0

Relationships components

This is the collection of relationships components within the ui-react-dom module. There is only one function, RelationshipInHtmlTable.

RelationshipInHtmlTable

The RelationshipInHtmlTable component renders the contents of the two Tables linked by a Relationship as an HTML <table> element, and registers a listener so that any changes to that result will cause a re-render.

RelationshipInHtmlTable(props: RelationshipInHtmlTableProps & HtmlTableProps): ComponentReturnType
TypeDescription
propsRelationshipInHtmlTableProps & HtmlTableProps

The props for this component.

returnsComponentReturnType

A rendering of the two Tables linked by a Relationship in a

<table> element.

See the <RelationshipInHtmlTable /> demo for this component in action.

The component's props identify which Relationship to render based on Relationship Id and Relationships object (which is either the default context Relationships object, a named context Relationships object, or an explicit reference).

This component renders the two Table objects by iterating over their related Row objects. By default the Cells are in turn rendered with the CellView component, but you can override this behavior by providing a component for each Cell in the customCells prop. You can pass additional props to that custom component with the getComponentProps callback. See the CustomCell type for more details.

Note the use of dotted 'tableId.cellId' string pairs when specifying custom rendering for the cells in this table, since Cells from both the relationship's 'local' and 'remote' Table objects can be rendered and need to be distinguished.

This component uses the useRowIds and useRemoteRowId hooks under the covers, which means that any changes to the structure of either Table resulting in a change to the relationship will cause a re-render.

You can use the headerRow and idColumn props to control whether labels and Ids appear in a <th> element at the top of the table, and the start of each row.

Examples

This example creates a Provider context into which a default Relationships object is provided. The RelationshipInHtmlTable component within it then renders the two Tables linked by a relationship in a <table> element with a CSS class. Note the dotted pairs that are used as column headings.

import {createRelationships, createStore} from 'tinybase';
import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {RelationshipInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <RelationshipInHtmlTable
    relationshipId="petSpecies"
    className="relationship"
  />
);

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'dog'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// ->
`
<table class="relationship">
  <thead>
    <tr>
      <th>pets.Id</th>
      <th>species.Id</th>
      <th>pets.species</th>
      <th>species.price</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>fido</th>
      <th>dog</th>
      <td>dog</td>
      <td>5</td>
    </tr>
    <tr>
      <th>cujo</th>
      <th>dog</th>
      <td>dog</td>
      <td>5</td>
    </tr>
  </tbody>
</table>
`;

This example creates a Provider context into which a default Relationships object is provided. The RelationshipInHtmlTable component within it then renders the two Tables linked by a relationship with a custom component and a custom props callback for the species Cell. The header row at the top of the table and the Id column at the start of each row is removed.

import {CellView, Provider} from 'tinybase/ui-react';
import {createRelationships, createStore} from 'tinybase';
import React from 'react';
import {RelationshipInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';

const App = ({relationships}) => (
  <Provider relationships={relationships}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <RelationshipInHtmlTable
    relationshipId="petSpecies"
    customCells={customCells}
    idColumn={false}
    headerRow={false}
  />
);

const FormattedCellView = ({tableId, rowId, cellId, store, bold}) => (
  <>
    {bold ? <b>{rowId}</b> : rowId}:
    <CellView
      tableId={tableId}
      rowId={rowId}
      cellId={cellId}
      store={store}
    />
  </>
);
const customCells = {
  'species.price': {
    component: FormattedCellView,
    getComponentProps: (rowId) => ({bold: rowId == 'dog'}),
  },
};

const relationships = createRelationships(
  createStore()
    .setTable('pets', {fido: {species: 'dog'}, cujo: {species: 'wolf'}})
    .setTable('species', {wolf: {price: 10}, dog: {price: 5}}),
).setRelationshipDefinition('petSpecies', 'pets', 'species', 'species');

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App relationships={relationships} />);
console.log(app.innerHTML);
// ->
`
<table>
  <tbody>
    <tr>
      <td><b>dog</b>:5</td>
    </tr>
    <tr>
      <td>wolf:10</td>
    </tr>
  </tbody>
</table>
`;
Since

v4.1.0

Store components

This is the collection of store components within the ui-react-dom module. There are 6 store components in total.

TableInHtmlTable

The TableInHtmlTable component renders the contents of a single Table in a Store as an HTML <table> element, and registers a listener so that any changes to that result will cause a re-render.

TableInHtmlTable(props: TableInHtmlTableProps & HtmlTableProps): ComponentReturnType
TypeDescription
propsTableInHtmlTableProps & HtmlTableProps

The props for this component.

returnsComponentReturnType

A rendering of the Table in a <table> element.

See the <TableInHtmlTable /> demo for this component in action.

The component's props identify which Table to render based on Table Id, and Store (which is either the default context Store, a named context Store, or by explicit reference).

This component renders a Table by iterating over its Row objects. By default the Cells are in turn rendered with the CellView component, but you can override this behavior by providing a component for each Cell in the customCells prop. You can pass additional props to that custom component with the getComponentProps callback. See the CustomCell type for more details.

This component uses the useRowIds hook under the covers, which means that any changes to the structure of the Table will cause a re-render.

You can use the headerRow and idColumn props to control whether the Ids appear in a <th> element at the top of the table, and the start of each row.

Examples

This example creates a Provider context into which a default Store is provided. The TableInHtmlTable component within it then renders the Table in a <table> element with a CSS class.

import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {TableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <TableInHtmlTable tableId="pets" className="table" />;

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<table class="table">
  <thead>
    <tr>
      <th>Id</th>
      <th>species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>fido</th>
      <td>dog</td>
    </tr>
    <tr>
      <th>felix</th>
      <td>cat</td>
    </tr>
  </tbody>
</table>
`;

This example creates a Provider context into which a default Store is provided. The TableInHtmlTable component within it then renders the Table with a custom component and a custom props callback for the species Cell. The header row at the top of the table and the Id column at the start of each row is removed.

import {CellView, Provider} from 'tinybase/ui-react';
import React from 'react';
import {TableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <TableInHtmlTable
    tableId="pets"
    customCells={customCells}
    headerRow={false}
    idColumn={false}
  />
);

const FormattedCellView = ({tableId, rowId, cellId, bold}) => (
  <>
    {bold ? <b>{rowId}</b> : rowId}:
    <CellView tableId={tableId} rowId={rowId} cellId={cellId} />
  </>
);
const customCells = {
  species: {
    component: FormattedCellView,
    getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
  },
};

const store = createStore().setTable('pets', {
  fido: {species: 'dog'},
  felix: {species: 'cat'},
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<table>
  <tbody>
    <tr>
      <td><b>fido</b>:dog</td>
    </tr>
    <tr>
      <td>felix:cat</td>
    </tr>
  </tbody>
</table>
`;
Since

v4.1.0

SortedTableInHtmlTable

The SortedTableInHtmlTable component renders the contents of a single sorted Table in a Store, as an HTML <table> element, and registers a listener so that any changes to that result will cause a re-render.

SortedTableInHtmlTable(props: SortedTableInHtmlTableProps & HtmlTableProps): ComponentReturnType
TypeDescription
propsSortedTableInHtmlTableProps & HtmlTableProps

The props for this component.

returnsComponentReturnType

A rendering of the Table in a <table> element.

See the <SortedTableInHtmlTable /> demo for this component in action.

The component's props identify which Table to render based on Table Id, and Store (which is either the default context Store, a named context Store, or by explicit reference). It also takes a Cell Id to sort by and a boolean to indicate that the sorting should be in descending order. The offset and limit props are used to paginate results, but default to 0 and undefined to return all available Row Ids if not specified.

This component renders a ResultTable by iterating over its Row objects, in the order dictated by the sort parameters. By default the Cells are in turn rendered with the CellView component, but you can override this behavior by providing a component for each Cell in the customCells prop. You can pass additional props to that custom component with the getComponentProps callback. See the CustomCell type for more details.

This component uses the useSortedRowIds hook under the covers, which means that any changes to the structure or sorting of the Table will cause a re-render.

You can use the headerRow and idColumn props to control whether the Ids appear in a <th> element at the top of the table, and the start of each row.

The sortOnClick prop makes the table's sorting interactive such that the user can click on a column heading to sort by that column. The style classes sorted and ascending (or descending) are added so that you can provide hints to the user how the sorting is being applied.

Provide a paginator component for the Table with the paginator prop. Set to true to use the default SortedTablePaginator, or provide your own component that accepts SortedTablePaginatorProps.

Finally, the onChange prop lets you listen to a user's changes to the Table's sorting or pagination.

Examples

This example creates a Provider context into which a default Store is provided. The SortedTableInHtmlTable component within it then renders the Table in a <table> element with a CSS class.

import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {SortedTableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <SortedTableInHtmlTable
    tableId="pets"
    cellId="species"
    className="table"
  />
);

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<table class="table">
  <thead>
    <tr>
      <th>Id</th>
      <th class="sorted ascending">↑ species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>felix</th>
      <td>cat</td>
    </tr>
    <tr>
      <th>fido</th>
      <td>dog</td>
    </tr>
  </tbody>
</table>
`;

This example creates a Provider context into which a default Store is provided. The SortedTableInHtmlTable component within it then renders the Table with a custom component and a custom props callback for the species Cell. The header row at the top of the table and the Id column at the start of each row is removed.

import {CellView, Provider} from 'tinybase/ui-react';
import React from 'react';
import {SortedTableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);

const Pane = () => (
  <SortedTableInHtmlTable
    tableId="pets"
    cellId="species"
    customCells={customCells}
    headerRow={false}
    idColumn={false}
  />
);

const FormattedCellView = ({tableId, rowId, cellId, bold}) => (
  <>
    {bold ? <b>{rowId}</b> : rowId}:
    <CellView tableId={tableId} rowId={rowId} cellId={cellId} />
  </>
);
const customCells = {
  species: {
    component: FormattedCellView,
    getComponentProps: (rowId) => ({bold: rowId == 'fido'}),
  },
};

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
  },
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<table>
  <tbody>
    <tr>
      <td>felix:cat</td>
    </tr>
    <tr>
      <td><b>fido</b>:dog</td>
    </tr>
  </tbody>
</table>
`;
Since

v4.1.0

SortedTablePaginator

The SortedTablePaginator component renders a paginator for a sorted table.

SortedTablePaginator(props: SortedTablePaginatorProps): ComponentReturnType
TypeDescription
propsSortedTablePaginatorProps

The props for this component.

returnsComponentReturnType

The rendering of a paginator control with a label, and next and previous buttons, where appropriate.

See the <SortedTableInHtmlTable /> demo for this component in action.

The component displays 'previous' and 'next' buttons for paging through the Table if there are more Row Ids than fit in each page. The component will also display a label that shows which Row Ids are being displayed.

The component's props identify initial pagination settings, and it will fire an event when the pagination changes.

Example

This example creates a Provider context into which a default Store is provided. The SortedTableInHtmlTable component within it then renders the Table in a <table> element with a SortedTablePaginator (the default).

import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {SortedTableInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <SortedTableInHtmlTable
    tableId="pets"
    cellId="species"
    limit={2}
    paginator={true}
  />
);

const store = createStore().setTables({
  pets: {
    fido: {species: 'dog'},
    felix: {species: 'cat'},
    cujo: {species: 'wolf'},
    lowly: {species: 'worm'},
    polly: {species: 'parrot'},
  },
});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<table>
  <caption>
    <button class="previous" disabled="">←</button>
    <button class="next">→</button>
    1 to 2 of 5 rows
  </caption>
  <thead>
    <tr>
      <th>Id</th>
      <th class="sorted ascending">↑ species</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>felix</th>
      <td>cat</td>
    </tr>
    <tr>
      <th>fido</th>
      <td>dog</td>
    </tr>
  </tbody>
</table>
`;
Since

v4.1.0

ValuesInHtmlTable

The ValuesInHtmlTable component renders the keyed value contents of a Store as an HTML <table> element, and registers a listener so that any changes to that result will cause a re-render.

ValuesInHtmlTable(props: ValuesInHtmlTableProps & HtmlTableProps): ComponentReturnType
TypeDescription
propsValuesInHtmlTableProps & HtmlTableProps

The props for this component.

returnsComponentReturnType

A rendering of the Values in a <table> element.

See the <ValuesInHtmlTable /> demo for this component in action.

The component's props identify which Row to render based on Table Id, Row Id, and Store (which is either the default context Store, a named context Store, or an explicit reference).

This component renders a Store by iterating over its Value objects. By default the Values are in turn rendered with the ValueView component, but you can override this behavior by providing a valueComponent prop, a custom component of your own that will render a Value based on ValueProps. You can also pass additional props to your custom component with the getValueComponentProps callback prop.

This component uses the useValueIds hook under the covers, which means that any changes to the structure of the Values in the Store will cause a re-render.

You can use the headerRow and idColumn props to control whether labels and Ids appear in a <th> element at the top of the table, and the start of each row.

Examples

This example creates a Provider context into which a default Store is provided. The ValuesInHtmlTable component within it then renders the Values in a <table> element with a CSS class.

import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {ValuesInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <ValuesInHtmlTable className="values" />;

const store = createStore().setValues({open: true, employees: 3});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<table class="values">
  <thead>
    <tr>
      <th>Id</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>open</th>
      <td>true</td>
    </tr>
    <tr>
      <th>employees</th>
      <td>3</td>
    </tr>
  </tbody>
</table>
`;

This example creates a Provider context into which a default Store is provided. The ValuesInHtmlTable component within it then renders the Row with a custom Cell component and a custom props callback. The header row at the top of the table and the Id column at the start of each row is removed.

import {Provider, ValueView} from 'tinybase/ui-react';
import React from 'react';
import {ValuesInHtmlTable} from 'tinybase/ui-react-dom';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const getBoldProp = (valueId) => ({bold: valueId == 'open'});
const Pane = () => (
  <ValuesInHtmlTable
    valueComponent={FormattedValueView}
    getValueComponentProps={getBoldProp}
    headerRow={false}
    idColumn={false}
  />
);
const FormattedValueView = ({valueId, bold}) => (
  <>
    {bold ? <b>{valueId}</b> : valueId}
    {': '}
    <ValueView valueId={valueId} />
  </>
);

const store = createStore().setValues({open: true, employees: 3});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<table>
  <tbody>
    <tr><td><b>open</b>: true</td></tr>
    <tr><td>employees: 3</td></tr>
  </tbody>
</table>
`;
Since

v4.1.0

EditableCellView

The EditableCellView component renders the value of a single Cell in a way that can be edited in a web browser, and registers a listener so that any changes to that result will cause a re-render.

EditableCellView(props: CellProps & {
  className?: string;
  showType?: boolean;
}): ComponentReturnType
TypeDescription
propsCellProps & { className?: string; showType?: boolean; }

The props for this component.

returnsComponentReturnType

An editable rendering of the Cell.

See the <EditableCellView /> demo for this component in action.

The component's props identify which Cell to render based on Table Id, Row Id, Cell Id, and Store (which is either the default context Store, a named context Store, or an explicit reference).

A Cell contains a string, number, or boolean, so the value is rendered in an appropriate <input> tag and a button lets the user change type, if possible.

Set the showType prop to false to remove the ability for the user to see or change the Cell type. They will also not be able to change the type if there is a TablesSchema applied to the Store.

This component uses the useCell hook under the covers, which means that any changes to the specified Cell outside of this component will cause a re-render.

You can provide a custom className prop which well be used on the root of the resulting element. If omitted the element's class will be editableCell. The debugIds prop has no effect on this component.

Example

This example creates a Provider context into which a default Store is provided. The EditableCellView component within it then renders an editable Cell.

import {EditableCellView} from 'tinybase/ui-react-dom';
import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => (
  <EditableCellView tableId="pets" rowId="fido" cellId="color" />
);

const store = createStore().setCell('pets', 'fido', 'color', 'brown');
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<div class="editableCell">
  <button class="string">string</button>
  <input value="brown">
</div>
`;
Since

v4.1.0

EditableValueView

The EditableValueView component renders the value of a single Value in a way that can be edited in a web browser, and registers a listener so that any changes to that result will cause a re-render.

EditableValueView(props: ValueProps & {
  className?: string;
  showType?: boolean;
}): ComponentReturnType
TypeDescription
propsValueProps & { className?: string; showType?: boolean; }

The props for this component.

returnsComponentReturnType

An editable rendering of the Value.

See the <EditableValueView /> demo for this component in action.

The component's props identify which Value to render based on Table Id, Row Id, Value Id, and Store (which is either the default context Store, a named context Store, or an explicit reference).

A Value contains a string, number, or boolean, so the value is rendered in an appropriate <input> tag and a button lets the user change type, if possible.

Set the showType prop to false to remove the ability for the user to see or change the Value type. They will also not be able to change the type if there is a ValuesSchema applied to the Store.

This component uses the useValue hook under the covers, which means that any changes to the specified Value outside of this component will cause a re-render.

You can provide a custom className prop which well be used on the root of the resulting element. If omitted the element's class will be editableValue. The debugIds prop has no effect on this component.

Example

This example creates a Provider context into which a default Store is provided. The EditableValueView component within it then renders an editable Value.

import {EditableValueView} from 'tinybase/ui-react-dom';
import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <EditableValueView valueId="employees" />;

const store = createStore().setValue('employees', 3);
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
console.log(app.innerHTML);
// ->
`
<div class="editableValue">
  <button class="number">number</button>
  <input type="number" value="3">
</div>
`;
Since

v4.1.0

Type Aliases

These are the type aliases within the ui-react-dom module.

Configuration type aliases

This is the collection of configuration type aliases within the ui-react-dom module. There are only two configuration type aliases, CustomCell and CustomResultCell.

CustomCell

The CustomCell object is used to configure custom cell rendering in an HTML table.

{
  label?: string;
  component?: ComponentType<CellProps>;
  getComponentProps?: (rowId: Id, cellId: Id) => ExtraProps;
}
TypeDescription
label?string

An optional string that will be used as the label at the top of the table column for this Cell.

component?ComponentType<CellProps>

An optional custom component for rendering each Cell in the Table (to override the default CellView component).

getComponentProps?(rowId: Id, cellId: Id) => ExtraProps

An optional function for generating extra props for each custom Cell component based on Row and Cell Id.

Since

v4.1.0

CustomResultCell

The CustomResultCell object is used to configure custom cell rendering for query results in an HTML table.

{
  label?: string;
  component?: ComponentType<ResultCellProps>;
  getComponentProps?: (rowId: Id, cellId: Id) => ExtraProps;
}
TypeDescription
label?string

An optional string that will be used as the label at the top of the table column for this Cell.

component?ComponentType<ResultCellProps>

An optional custom component for rendering each Cell in the ResultTable (to override the default ResultCellView component).

getComponentProps?(rowId: Id, cellId: Id) => ExtraProps

An optional function for generating extra props for each custom Cell component based on Row and Cell Id.

Since

v4.1.0

Props type aliases

This is the collection of props type aliases within the ui-react-dom module. There are 9 props type aliases in total.

TableInHtmlTableProps

TableInHtmlTableProps props are used for components that will render a Table in an HTML table, such as the TableInHtmlTable component.

{
  tableId: Id;
  store?: StoreOrStoreId;
  editable?: boolean;
  customCells?: Ids | {[cellId: Id]: string | CustomCell};
}
TypeDescription
tableIdId

The Id of the Table in the Store to be rendered.

store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

editable?boolean

Whether the Cells should be editable. This affects the default CellView component (to use the EditableCellView component instead) but of course will not affect custom Cell components if you have set them.

customCells?Ids | {[cellId: Id]: string | CustomCell}

An optional list of Cell Ids to use for rendering a prescribed set of the Table's Cells in a given order. This can also be an object with the desired Cell Ids as keys, and with a value that can either be a string label to show in the column header, or a CustomCell object to further configure the column.

Since

v4.1.0

HtmlTableProps

HtmlTableProps props are used for components that will render in an HTML table, such as the TableInHtmlTable component or SortedTableInHtmlTable component.

{
  className?: string;
  headerRow?: boolean;
  idColumn?: boolean;
}
TypeDescription
className?string

A string className to use on the root of the resulting element.

headerRow?boolean

Whether a header row should be rendered at the top of the table, defaulting to true.

idColumn?boolean

Whether an Id column should be rendered on the left of the table, defaulting to true.

Since

v4.1.0

RelationshipInHtmlTableProps

RelationshipInHtmlTableProps props are used for components that will render the contents of the two Tables linked by a Relationship as an HTML table, such as the RelationshipInHtmlTable component.

{
  relationshipId: Id;
  relationships?: RelationshipsOrRelationshipsId;
  editable?: boolean;
  customCells?: Ids | {[cellId: Id]: string | CustomCell};
}
TypeDescription
relationshipIdId

The Id of the relationship in the Relationships object for which the relationship Table Rows will be rendered.

relationships?RelationshipsOrRelationshipsId

The Relationships object to be accessed: omit for the default context Relationships object, provide an Id for a named context Relationships object, or provide an explicit reference.

editable?boolean

Whether the Cells should be editable. This affects the default CellView component (to use the EditableCellView component instead) but of course will not affect custom Cell components if you have set them.

customCells?Ids | {[cellId: Id]: string | CustomCell}

An optional list of dotted 'tableId.cellId' string pairs to use for rendering a prescribed set of the relationship Tables' Cells in a given order. This can also be an object with the desired 'tableId.cellId' string pairs as keys, and with a value that can either be a string label to show in the column header, or a CustomCell object to further configure the column.

Note the use of dotted 'tableId.cellId' string pairs when specifying custom rendering for the cells in this table, since Cells from both the relationship's 'local' and 'remote' Table objects can be rendered and need to be distinguished.

Since

v4.1.0

ResultSortedTableInHtmlTableProps

ResultSortedTableInHtmlTableProps props are used for components that will render a sorted Table in an HTML table, such as the SortedTableInHtmlTable component.

{
  queryId: Id;
  cellId?: Id;
  descending?: boolean;
  offset?: number;
  limit?: number;
  queries?: QueriesOrQueriesId;
  customCells?: Ids | {[cellId: Id]: string | CustomResultCell};
  sortOnClick?: boolean;
  paginator?: boolean | ComponentType<SortedTablePaginatorProps>;
  onChange?: (sortAndOffset: [cellId: Id | undefined, descending: boolean, offset: number]) => void;
}
TypeDescription
queryIdId

The Id of the query in the Queries object for which the ResultTable will be rendered.

cellId?Id

The Id of the Cell whose values are used for the sorting. If omitted, the view will sort the Row Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of Row Ids to skip for pagination purposes.

limit?number

The maximum number of Row Ids to return.

queries?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

customCells?Ids | {[cellId: Id]: string | CustomResultCell}

An optional list of Cell Ids to use for rendering a prescribed set of the ResultTable's Cells in a given order. This can also be an object with the desired Cell Ids as keys, and with a value that can either be a string label to show in the column header, or a ResultCustomCell object to further configure the column.

sortOnClick?boolean

Whether the table should be interactive such that clicking a header changes the sorting and/or direction.

paginator?boolean | ComponentType<SortedTablePaginatorProps>

Either true to show the default SortedTablePaginator for the ResultTable, or provide your own paginator component that takes SortedTablePaginatorProps.

onChange?(sortAndOffset: [cellId: Id | undefined, descending: boolean, offset: number]) => void

A function that is called whenever the sorting or pagination of the ResultTable is changed by the user, invoked with the sorted Cell Id, whether descending or not, and the offset of the pagination.

Since

v4.1.0

ResultTableInHtmlTableProps

ResultTableInHtmlTableProps props are used for components that will render a ResultTable in an HTML table, such as the ResultTableInHtmlTable component.

{
  queryId: Id;
  queries?: QueriesOrQueriesId;
  customCells?: Ids | {[cellId: Id]: string | CustomResultCell};
}
TypeDescription
queryIdId

The Id of the query in the Queries object for which the ResultTable will be rendered.

queries?QueriesOrQueriesId

The Queries object to be accessed: omit for the default context Queries object, provide an Id for a named context Queries object, or provide an explicit reference.

customCells?Ids | {[cellId: Id]: string | CustomResultCell}

An optional list of Cell Ids to use for rendering a prescribed set of the ResultTable's Cells in a given order. This can also be an object with the desired Cell Ids as keys, and with a value that can either be a string label to show in the column header, or a ResultCustomCell object to further configure the column.

Since

v4.1.0

SliceInHtmlTableProps

SliceInHtmlTableProps props are used for components that will render an Index Slice in an HTML table, such as the SliceInHtmlTable component.

{
  indexId: Id;
  sliceId: Id;
  indexes?: IndexesOrIndexesId;
  editable?: boolean;
  customCells?: Ids | {[cellId: Id]: string | CustomCell};
}
TypeDescription
indexIdId

The Id of the Index in the Indexes object.

sliceIdId

The Id of the Slice in the Index to be rendered.

indexes?IndexesOrIndexesId

The Indexes object to be accessed: omit for the default context Indexes object, provide an Id for a named context Indexes object, or provide an explicit reference.

editable?boolean

Whether the Cells should be editable. This affects the default CellView component (to use the EditableCellView component instead) but of course will not affect custom Cell components if you have set them.

customCells?Ids | {[cellId: Id]: string | CustomCell}

An optional list of Cell Ids to use for rendering a prescribed set of the Slice's Cells in a given order. This can also be an object with the desired Cell Ids as keys, and with a value that can either be a string label to show in the column header, or a CustomCell object to further configure the column.

Since

v4.1.0

SortedTableInHtmlTableProps

SortedTableInHtmlTableProps props are used for components that will render a sorted Table in an HTML table, such as the SortedTableInHtmlTable component.

{
  tableId: Id;
  cellId?: Id;
  descending?: boolean;
  offset?: number;
  limit?: number;
  store?: StoreOrStoreId;
  editable?: boolean;
  customCells?: Ids | {[cellId: Id]: string | CustomCell};
  sortOnClick?: boolean;
  paginator?: boolean | ComponentType<SortedTablePaginatorProps>;
  onChange?: (sortAndOffset: [cellId: Id | undefined, descending: boolean, offset: number]) => void;
}
TypeDescription
tableIdId

The Id of the Table in the Store to be rendered.

cellId?Id

The Id of the Cell whose values are used for the sorting. If omitted, the view will sort the Row Id itself.

descending?boolean

Whether the sorting should be in descending order.

offset?number

The number of Row Ids to skip for pagination purposes.

limit?number

The maximum number of Row Ids to return.

store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

editable?boolean

Whether the Cells should be editable. This affects the default CellView component (to use the EditableCellView component instead) but of course will not affect custom Cell components if you have set them.

customCells?Ids | {[cellId: Id]: string | CustomCell}

An optional list of Cell Ids to use for rendering a prescribed set of the Table's Cells in a given order. This can also be an object with the desired Cell Ids as keys, and with a value that can either be a string label to show in the column header, or a CustomCell object to further configure the column.

sortOnClick?boolean

Whether the table should be interactive such that clicking a header changes the sorting and/or direction.

paginator?boolean | ComponentType<SortedTablePaginatorProps>

Either true to show the default SortedTablePaginator for the Table, or provide your own paginator component that takes SortedTablePaginatorProps.

onChange?(sortAndOffset: [cellId: Id | undefined, descending: boolean, offset: number]) => void

A function that is called whenever the sorting or pagination of the Table is changed by the user, invoked with the sorted Cell Id, whether descending or not, and the offset of the pagination.

Since

v4.1.0

SortedTablePaginatorProps

SortedTablePaginatorProps props are used for components that will be used as a table paginator, such as the SortedTablePaginator component.

{
  onChange: (offset: number) => void;
  offset?: number;
  limit?: number;
  total: number;
  singular?: string;
  plural?: string;
}
TypeDescription
onChange(offset: number) => void

An event that will fire when the offset is updated, called with the new offset.

offset?number

The number of Row Ids to skip for pagination.

limit?number

The maximum number of Row Ids being returned.

totalnumber

The total number of Row Ids in the paginated table.

singular?string

A noun to use in the pagination label for a single row, defaulting to 'row'.

plural?string

A noun to use in the pagination label for multiple rows, defaulting to the value of the singular noun suffixed with the letter 's'.

Since

v4.1.0

ValuesInHtmlTableProps

ValuesInHtmlTableProps props are used for components that will render Values in an HTML table, such as the ValuesInHtmlTable component.

{
  store?: StoreOrStoreId;
  editable?: boolean;
  valueComponent?: ComponentType<ValueProps>;
  getValueComponentProps?: (valueId: Id) => ExtraProps;
}
TypeDescription
store?StoreOrStoreId

The Store to be accessed: omit for the default context Store, provide an Id for a named context Store, or provide an explicit reference.

editable?boolean

Whether the Values should be editable. This affects the default ValueView component (to use the EditableValueView component instead) but of course will not affect a custom valueComponent if you have set one.

valueComponent?ComponentType<ValueProps>

A custom component for rendering each Value in the Store (to override the default ValueView component).

getValueComponentProps?(valueId: Id) => ExtraProps

A function for generating extra props for each custom Value component based on its Id.

Since

v4.1.0

ui-react-inspector

The ui-react-inspector module of the TinyBase project provides a component to help debug the state of your TinyBase stores and other objects.

The component in this module uses the react-dom module and so is not appropriate for environments like React Native.

See also

<Inspector /> demo

Since

v5.0.0

Functions

There is one function, Inspector, within the ui-react-inspector module.

Inspector

The Inspector component renders a tool which allows you to view and edit the content of a Store in a debug web environment.

Inspector(props: InspectorProps): ComponentReturnType
TypeDescription
propsInspectorProps

The props for this component.

returnsComponentReturnType

The rendering of the inspector tool.

See the <Inspector /> demo for this component in action.

The component displays a nub in the corner of the screen which you may then click to interact with all the Store objects in the Provider component context.

The component's props identify the nub's initial location and panel state, though subsequent user changes to that will be preserved on each reload.

Example

This example creates a Provider context into which a default Store is provided. The Inspector component within it then renders the inspector tool.

import {Inspector} from 'tinybase/ui-react-inspector';
import {Provider} from 'tinybase/ui-react';
import React from 'react';
import {createRoot} from 'react-dom/client';
import {createStore} from 'tinybase';

const App = ({store}) => (
  <Provider store={store}>
    <Pane />
  </Provider>
);
const Pane = () => <Inspector />;

const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
const app = document.createElement('div');
createRoot(app).render(<App store={store} />);
// ...
console.log(app.innerHTML.substring(0, 30));
// -> '<aside id="tinybaseInspector">'
Since

v5.0.0

Type Aliases

There is one type alias, InspectorProps, within the ui-react-inspector module.

InspectorProps

InspectorProps props are used to configure the Inspector component.

{
  position?: "top" | "right" | "bottom" | "left" | "full";
  open?: boolean;
}
TypeDescription
position?"top" | "right" | "bottom" | "left" | "full"

An optional string to indicate where you want the inspector to first appear.

open?boolean

An optional boolean to indicate whether the inspector should start in the opened state.

Since

v5.0.0