svelte-writable-derived
For users of Svelte v3, this is a read-write variant of Svelte's derived stores that accepts an extra callback to send values back to the source. It builds on the derived & writable stores provided by Svelte, and emulates their behavior as closely as possible.
This project has a Code of Conduct. By participating in the Git repo or issues tracker, you agree to be as courteous, welcoming, and generally a lovely person as its terms require. 😊
Default export: writableDerived()
Parameters: origins
(store or array of stores), derive
(function), reflect
(function), optional initial
(any)
Returns a store with writable
methods
Create a store that behaves similarly to Svelte's derived
, with origins
, derive
, and initial
working like its 1st, 2nd, and 3rd parameters respectively. Values introduced to the store via its set
and update
methods are passed to the new 3rd parameter, reflect
, which can in turn set values for the origin stores.
It is not possible for derived
and reflect
to trigger calls to each other, provided they only use the set
callbacks provided to them and do not reach out to any outer set
or update
.
New parameter: reflect()
Called with: object with reflecting
, old
, and set
properties
Return value varies (see below)
Called when the derived store is given a new value via its set
and update
methods (not via the derive
callback), and can set new values on the origin stores without causing a call to derive
.
reflect
will be called before any of the derived store's subscriptions are called. If this results in any origin stores being set synchronously, their subscriptions will also be called before the derived store's subscriptions.
reflect
is called with one parameter, an object that has these properties:
Name | Description |
---|
reflecting | The new value of the derived store. |
old | The initial value of the origin stores. It's an array if origins was an array. (This is an accessor property, and has special behavior for derived stores with no subscriptions.) |
set() | If origins is a single store, this takes its new value. If origins is an array of stores, this takes an array of values to set in each store. If the array you pass in is sparse or shorter than origins , this sets only the stores it has elements for, and other stores don't necessarily need to be writable. (This is an accessor property, and affects the treatment of the return value as per below.) |
If the set
property was not read, reflect
is considered synchronous, and its return value will be used to set origin stores just as if it were passed to set()
. If the set
property was read, reflect
is considered asynchronous, and its return value, if it's a function, is a cleanup function that will be called before the next reflect
call. (Unlike its derive
counterpart, reflect
's cleanup function is never called in response to unsubscriptions.)
It is recommended that your reflect
function use a destructuring parameter, like so:
var coolStore = writableDerived(origins, derive, reflectExample, initial);
function reflectExample({reflecting, old, set}) {
return cleanup;
}
Regarding Subscription-less writableDerived
Stores
One of the ways writableDerived
emulates the behavior of Svelte's derived
is that it does not subscribe to any origin store until the derived store itself has a subscription. However, writableDerived
makes an exception in certain situations to guarantee that values of interest are up-to-date.
When the derived store has no subscriptions, performing these operations will subscribe to & then unsubscribe from all its origins:
- Calling the derived store's
update
method - Getting the
old
property of the object passed to reflect
Examples
Making an object store from a JSON string store
import { writable, get } from "svelte/store";
import writableDerived from "svelte-writable-derived";
var jsonStore = writable(`{"I'm a property": true}`);
var objectStore = writableDerived(
jsonStore,
(json) => JSON.parse(json),
({reflecting}) => JSON.stringify(reflecting)
);
console.log( Object.keys( get(objectStore) ) );
objectStore.set({"I'm not a property": false});
console.log( get(jsonStore) );
Making a single-value store from an object store
import { writable, get } from "svelte/store";
import writableDerived from "svelte-writable-derived";
var objectStore = writable({"a horse": "a horse", "of course": "of course"});
var valueStore = writableDerived(
objectStore,
(object) => object["a horse"],
({reflecting, old}) => {
old["a horse"] = reflecting;
return old;
}
);
console.log( get(valueStore) );
valueStore.set("*whinny*");
console.log( get(objectStore) );
Making an object store from several single-value stores
import { writable, get } from "svelte/store";
import writableDerived from "svelte-writable-derived";
var valueStore1 = "sparta", valueStore2 = "monty python's flying circus";
var objectStore = writableDerived(
[valueStore1, valueStore2],
([value1, value2]) => ( {"this is": value1, "it's": value2} ),
({reflecting}) => [ reflecting["this is"], reflecting["it's"] ]
);
console.log( get(objectStore) );
objectStore.set( {"this is": "rocket league", "it's": "over 9000"} );
console.log( get(valueStore1), get(valueStore2) );
Chaining all of the above together
import { writable, get } from "svelte/store";
import writableDerived from "svelte-writable-derived";
var jsonStore = writable(`{"owner": "dragon", "possessions": ["crown", "gold"]}`);
var hoardStore = writableDerived(
jsonStore,
(json) => JSON.parse(json),
({reflecting}) => JSON.stringify(reflecting)
);
var hoarderStore = writableDerived(
objectStore,
(hoard) => hoard["owner"],
({reflecting, old}) => {
old["owner"] = reflecting;
return old;
}
);
var hoardContentsStore = writableDerived(
objectStore,
(hoard) => hoard["possessions"],
({reflecting, old}) => {
old["possessions"] = reflecting;
return old;
}
);
var itemListStore = writableDerived(
[hoarderStore, hoardContentsStore],
([hoarder, hoardContents]) => {
return hoardContents.map( (item) => {
return {item, owner: "hoarder"};
});
},
({reflecting}) => {
var hoarder = reflecting[0].owner;
var hoardContents = reflecting.map( (itemListEntry) => {
return itemListEntry["item"];
} );
return [hoarder, hoardContents];
}
);
jsonStore.subscribe(console.log);
hoardStore.subscribe(console.log);
hoarderStore.subscribe(console.log);
itemListStore.subscribe(console.log);
itemListStore.update( (itemList) => {
return itemList.map( (itemListEntry) => {
return {item: itemListEntry.item, owner: "protagonist"};
} );
} );
Browser compatibility
This package should run anywhere Svelte can run. Use transpilers/polyfills as needed.
💖 Support the developer
I muchly appreciate any way you'd like to show your thanks - knowing people are helped gives me warm fuzzies and makes it all worthwhile!
💸 ... with money
I'm on Ko-Fi! If you'd like to make a recurring donation, first please help me afford Ko-Fi Gold!
💌 ... with kind words
Current contact info is on this page - or you can create an "issue" on this repo just to say thanks! Thank-you "issues" will be closed right away, but are treasured regardless~
🤝 ... with a job
I have a Developer Story on Stack Overflow!