vyce

A tiny store you can use to build tidy relationships.

import { store, computed } from 'vyce';

const authors = store([
    { name: 'haruki', age: 25 },
    { name: 'james', age: 32 },
    { name: 'agatha', age: 62 }
]);

const youngAuthors = computed([authors], xs =>
    xs.filter(author => author.age < 40)
);

authors(prev => [
    ...prev,
    { name: 'david', age: 28 },
    { name: 'lovecraft', age: 57 }
]);

youngAuthors();
// [
//  { name: 'haruki', age: 25 },
//  { name: 'james', age: 32 },
//  { name: 'david', age: 28 }
// ]

Install

Node

npm install vyce

Deno

import { store, computed } from 'https://deno.land/x/vyce/index.js';

Browser

<script src="https://unpkg.com/vyce/dist/vyce.min.js"></script>

In the browser context, the default export name is vyce.

Browser (ESM)

<script type="module">
  import { store, computed } from 'https://unpkg.com/vyce/dist/vyce.js';
</script>

Try on Flems.io.

Usage

See index.d.ts for type definitions.

By default, stores created with vyce use a built-in deep clone function adapted from klona. The default function is capable of cloning objects with JSON-valid data types. You may opt to use another deep clone utility by passing a second argument to store should you have the need to clone more complex data types. See below for an example using klona/full.

import { store } from 'vyce';
import { klona } from 'klona/full';

const state = store({ name: 'denam' }, klona);

API

store()

import { store } from 'vyce';

const state = store({ name: 'denam' });

// call your store without arguments to get the value
state(); // `{ name: 'denam' }`

// pass an argument to set its value
state({ age: 18 });

// or pass a function to set a value based on the previous value
state(prev => ({ ...prev, name: 'catiua' }));

state(); // `{ age: 18, name: 'catiua' }`

store.sub

import { store } from 'vyce';

const state = store(10);
const unsub = state.sub(value => console.log(value)); // logs `10`

state(20); // logs `20`
unsub();
state(30); // does not log anything

Note: by default, the subscriber function is called once upon subscribing. Pass a falsey value as a second argument to store.sub to disable the initial call.

import { store } from 'vyce';

const state = store(10);
const unsub = state.sub(value => console.log(value), false); // does not log

state(20); // logs `20`

Note that setting a store will only update its value and run subscriptions if the new value is different than the old value. Internally, this is determined by using the === operator, which means passing the same reference to an object twice will not trigger an update even if that object has changed properties. In these instances, you can force an update by passing a shallow copy.

import { store } from 'vyce';

const obj = { lanselot: 1 };
const state = store(obj);
const unsub = state.sub(value => console.log(value)); // logs `{ lanselot: 1 }`

obj.warren = 2;
state(obj); // will not log
state({ ...obj }); // logs `{ lanselot: 1, warren: 2 }`

store.end

Calling end will release all subscriptions and clean up dependency stores, meaning subscriber functions will no longer be called upon updating the store.

import { store, computed } from 'vyce';

const foo = store(10);
const bar = store(20);

const rum = computed([foo, bar], (x, y) => x + y); // 30
const ham = computed([rum, bar], (x, y) => x + y); // 50

rum.end(); // breaks all listeners (ham), and also stops listening to foo and bar

bar(100); // does not affect rum, but *does* affect ham
console.log(rum()); // logs `30`

rum(0); // does not affect ham
console.log(ham()); // logs `130`

Credits

Inspired by flyd, klona, and Svelte Stores.