@composi/datastore:

DataStore

Storing Data

DataStores are for storing data. The DataStore class creates an immutable clone of the data provided. Since dataStores are about immutability, it doesn't make sense to have data that cannot be immutable. We're talking about native DOM elements, etc. You would not use data with references to the DOM since these are always by reference, not by value.

Creating a DataStore

First, you need to import the DataStore class into your project:

import { DataStore } from '@composi/data-store'

After that you are ready to create a dataStore. This can take upto two arguments: the data to use for state and an optional custom event.

Just pass the data you want to use in a dataStore to the DataStore constructor. You'll be able to access the dataStore's data from its state property:

import { DataStore } from '@composi/data-store'

const dataStore = new DataStore({name: 'Sarah'})
const name = dataStore.state.name
// name will have the value 'Sarah'

You can assign any valid JavaScript data type to the dataStore's state: null, undefined, false, number, string, array, object. In most cases you'll have a complex object as the dataStore's state.

watch

You use the watch method to watch for a custom event. The watch can also capture the updated state of the dataStore. A watcher fires a callback, which you can use to do whatever you need to do. For practical purposes this might be to update a component, or persist the data somehow.

dataStore.watch('special-event', data => render(, 'body')
})

setState

You can use the dataStore's setState to update its state. When you do so the dataStore notifies all of its watchers, sending them the updated state. Depending on how they are set up, the watchers can do something with the data, or merely react to the event.

When you use setState, the callback gets the previous state passed as its argument.

dataStore.setState(prevState => {
  prevState.names.push('Jessica')
  return prevState
})

send

dataStores are not just passive watchers. You can have the dataStore send an event and arbitrary data for that watcher to use.

dataStore.send('update-component', {name: 'Jeffrey'})

unwatch

Sometimes you may want a temporary watcher that you can dispose of later. You can do this using the dataStore unwatch method. This takes one argument: the event to unwatch. After unwatching an event, any watchers expecting that event will no longer work.

dataStore.unwatch('temp-event')

Data Persistence

DataStore has no builtin means of remote persistence. To do that the best way is to create an observer to watch for a special persist event. Then you would make a connection to the remote server to persist the data. To learn how to use observer, read the documentation for @composi/observer.

DataStore does provide a way to persist data locally, using the browser's localStorage. To learn more about that, check out the documentation for putInLocalStorage and getFromLocalStorage

Versioning

When you create a dataStore, it has a version of 1. You can increase the version with the bumpVersion method:

dataStore.version // returns 1
dataStore.bumpVersion()
dataStore.version // returns 2

bumpVersion always increments the version by 1.

Whenever you save the dataStore state to localStorage with putInLocalStorage, it also saves the current version with the key composi-store-version. You can retrieve this version during page load to see what version the dataStore was at when the last session ended:

const key = localStorage.getItem('composi-datastore-version')
if (version < 10) {
  console.log('The dataStore\'s version is too old.')
}

Timestamps

When you save a dataStore's state with putInLocalStorage it also saves a timestamp for the save with the key composi-datatore-timestamp. During page load you can retrieve the timestamp to see when was the last time the dataStore was saved. If it's too old you may want to clear localStorage to populate it with fresher data.

// During page load:
const timestamp = localStorage.getItem('composi-datastore-timestamp')
const currentTime = new Date().getTime()
// One day in milliseconds.
const day = 1000 * 60 * 60 * 24

// Test to see if timestamp is older than 30 days.
// If it is, clear localStorage:
if ((currentTime - timestamp) / day > 30) {
  // Remove the dataStore's key/value.
  localStorage.removeItem('composi-datastore')
  // Or clear the entire localStorage cache:
  localStorage.clear()
}

Summary

If you are already familiar with @composi/observer, you might have noticed that DataStore shares many features: watch, send and unwatch. That's because internally DataStore uses an observer to watch for changes and react to those when the occur, as well as the ability to send and unwatch its events. Although these features in dataStores make them more versatile, there may be situations where it makes more sense to use an observer in conjunction with a dataStore. That's because of their differences. A dataStore can register events and watch them. And you can send to a particular dataStore event to trigger it. But whenever the dataStore's state changes, ALL of its events are executed. In contrast, with a dedicated observer, you can also register as many custom events as you need. For observers, when you send to an event, only that one will fire.

Although dataStore provides a way to persist its state in localStorage, this may be inadequate for your needs. If that's the case, consider using @composi/idb. This is a promise-base wrapper for IndexedDB with a simple interface like localStorage.