device-storage-sharable-records
v1.0.1
Published
Device stored collection of sharable records.
Maintainers
Readme
device-storage sharable records
A local collection of records, stored in the device (browser), where any record can be shared as a URL.
As an example of usage, this module can be used to store personal preference settings, like a view setup (e.g. dashboard), but also be able to share this setup with someone else, and when doing so the receiver will get a copy of the setup stored in his/her local collection and own that piece of data in his/her device/browser.
Use this when:
- You're OK with letting the users own and handle certain settings instead of storing it centrally in a server.
- You don't control the backend but need to persist some (non-critical) dynamic dataset at the user.
To create a device storage sharable records manager, or local collection, it is recommended to input some settings like such:
const recordSchema = { title: '', items: [] };
const shallowProjection = ['title'];
const recordPrefix = 'ITEM.';
const localCollection = createLocalCollection({
recordSchema,
shallowProjection,
recordPrefix,
});recordSchema: This is used when creating new records. (idis added internaly)shallowProjection: The properties that are needed to present the collection of possible records to the user. (Read aboutrecordspart of the manager state, below)recordPrefix: This prefix makes it possible to have several separate local collections. (Since localStorage is shared among all websites on the same domain, this can ensure that each site uses a unique prefix)
The object that is returned from createLocalCollection contains first and foremost:
useLocalCollection: The main hook for the (zustand) internal state. See below for what the internal state contains.useCurrentRecord: A helper hook for thecurrentrecord.useRecords: A helper hook for the shallowrecordsarray.
Also, for non-React environments or integrations, some util functions are returned. Look below.
The internal (zustand) state contains:
current: The entire record currently selected.records: Array of all records in storage, with only propertiesidplus what's inshallowProjection.dUrl: The generated URL fromcurrentrecord.
And the actions available are:
init: Must be run, e.g. at application startup. Reads the persistant local storage and populatescurrent&records. Give this function a URL (or only the search/arguments part of it) to import an exported record.updateCurrent: Takes a record object and updates thecurrentrecord.createRecord: Creates a new record and sets it ascurrent.switchRecord: Give this function anidand it will search for it in the storage and set it ascurrent. If it is given theNEW_RECORD_KEYexported from this module, it will instead create a new record (same ascreateRecord).deleteRecord: Given anidthis will remove that record (from storage andrecords) and if that record is the current it will select a random record ascurrent, or create a new one if no other exists.exportToUrl: Export thecurrentrecord to a URL, that will be put indUrl.
API
Options input
See description about these above.
recordSchema:objectshallowProjection:arrayrecordPrefix:string
It is also possible to replace the default localStorage use, by providing storageFns as option:
storageFns: An object withstorageGet:(key: string) => stringstorageSet:(key: string, value: string) => boolstorageRemove:(key: string) => boolstorageGetAll:() => object
And lastly, if globalThis.location?.href.split('?')[0] is not the way to get the base URL for your environment (if you want to use the share feature), add this to options:
getBaseUrl:() => string,
createLocalCollection return
See description about these above.
useLocalCollection:(selector: function) => any(See zustand docs for details).useCurrentRecord:() => objectuseRecords:() => records
For non-React environments or integrations, some util functions are returned:
getStore:() => useLocalCollection.getState()setState:(updater) => useLocalCollection.setState(updater)subscribe:useLocalCollection.subscribedestroy:useLocalCollection.destroygetNewRecord:reducer.getNewRecordgetSelectedRecordId:reducer.getSelectedRecordId
To integrate this into other state managers, you could use some listener. Here is an example for binding to an XState (v5+) machine:
import { createLocalCollection } from 'device-storage-sharable-records';
export const localCollection = createLocalCollection(/* As above */);
const localCollectionListener = fromCallback(({ sendBack }) => {
localCollection.subscribe(({ current = {}, dUrl } = {}, prevState = {}) => {
console.log('localCollectionListener event:', current.id, dUrl);
// A record is imported:
if (current.id !== prevState.current?.id) return sendBack({ type: 'init', output: current });
// A record is exported:
if (dUrl && dUrl !== prevState.dUrl) return (globalThis.location = dUrl);
return undefined;
});
localCollection.getStore().init(globalThis.location.search);
return localCollection.destroy;
});Licence
MIT
