svelte-writable-derived
Advanced tools
Comparing version 1.0.1 to 2.0.0
@@ -0,5 +1,18 @@ | ||
## 2.0.0 (June 13, 2019) | ||
- Changed: `reflect` is now either a function with signature `(reflecting, set)` or an object with interface `{ withOld(reflecting, old, set) }`. Async-ness is determined by whether the function accepts a `set` parameter, just as for the `derive` callback. | ||
- Changed: The `reflect` callback now runs *after* subscriptions. Additional `set`/`update` calls while running subscriptions result in `reflect` being called only once with the most recent value. | ||
- Changed: Changes to which operations cause a subscription-less `writableDerived` to subscribe-then-unsubscribe to all origins: | ||
- `set` method calls now do so | ||
- Getting old origin values in `reflect` no longer does so | ||
- Optimization: Origin values are no longer kept if `reflect` will not need them | ||
- Optimization: Participates in Svelte v3.5.0's [diamond dependencies solution](https://github.com/sveltejs/svelte/pull/2955) (downstream only; there is no diamond dependency handling for reflecting values back to their sources) | ||
- Optimization: No more internal writable store; the `derived` is the only internal store remaining | ||
If this update was useful for you, show your appreciation with [money](README.md#--with-money), [kind words](README.md#--with-kind-words), or [a job](README.md#--with-a-job)! | ||
## 1.0.1 (June 7, 2019) | ||
- Fixed: First subscription was getting its first call before `derive` ran | ||
- Optimization: Internal derived store is no longer set; all sets go to the internal writable store | ||
- Optimization: Internal derived store is no longer set; all sets go to the internal writable store (replaced with a different optimization in 2.0.0) | ||
@@ -6,0 +19,0 @@ ## 1.0.0 (June 2, 2019) |
{ | ||
"name": "svelte-writable-derived", | ||
"version": "1.0.1", | ||
"version": "2.0.0", | ||
"description": "Two-way data-transforming stores for Svelte", | ||
"keywords": ["svelte", "svelte3"], | ||
"keywords": [ | ||
"svelte", | ||
"svelte3" | ||
], | ||
"author": { | ||
@@ -13,3 +16,5 @@ "name": "Pikadude No. 1", | ||
"module": "index.mjs", | ||
"files": ["index.mjs"], | ||
"files": [ | ||
"index.mjs" | ||
], | ||
"scripts": { | ||
@@ -24,5 +29,5 @@ "test": "mocha test.js" | ||
"mocha": "^6.1.4", | ||
"svelte": "^3.4.1" | ||
"svelte": "^3.5.1" | ||
}, | ||
"license": "MIT" | ||
} |
@@ -12,3 +12,3 @@ # svelte-writable-derived | ||
* [Default export: `writableDerived()`](#default-export-writablederived) | ||
+ [New parameter: `reflect()`](#new-parameter-reflect) | ||
+ [New parameter: `reflect`](#new-parameter-reflect) | ||
* [Regarding Subscription-less `writableDerived` Stores](#regarding-subscription-less-writablederived-stores) | ||
@@ -30,3 +30,3 @@ * [Examples](#examples) | ||
<i>Parameters: `origins` ([store](https://svelte.dev/tutorial/writable-stores) or array of stores), `derive` (function), [`reflect`](#new-parameter-reflect) (function), optional `initial` (any)</i><br> | ||
<i>Parameters: `origins` ([store](https://svelte.dev/tutorial/writable-stores) or array of stores), `derive` (function), [`reflect`](#new-parameter-reflect) (see documentation), optional `initial` (any)</i><br> | ||
<i>Returns a store with [`writable`](https://svelte.dev/docs#writable) methods</i> | ||
@@ -38,41 +38,20 @@ | ||
### New parameter: `reflect()` | ||
### New parameter: `reflect` | ||
<i>Called with: object with `reflecting`, `old`, and `set` properties</i> | ||
<i>Return value varies (see below)</i> | ||
<i>One of the following:</i> | ||
* <i>Function with parameters: `reflecting` (any), optional `set` (function)</i> | ||
* <i>Object with property `withOld` containing function with parameters: `reflecting` (any), `old` (any), optional `set` (function)</i> | ||
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`. | ||
The provided function is called when the derived store gets a new value via its `set` and `update` methods (not via the `derive` callback). Its `reflecting` parameter is this new value. The `set` parameter accepts a value to set in the origin store, if `origins` was a store, or an array of values to set if `origins` was an array. If the `set` parameter receives an array that's sparse or shorter than `origins`, it will only set the stores it has elements for, and other stores don't necessarily need to be writable. If the function doesn't take a `set` parameter, its return value will be used to set origin stores just as if it were passed to `set`. | ||
`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 after the derived store's subscriptions are called. If the derived store has its `set` and/or `update` methods called again in the process of calling its subscriptions, `reflect` will be called only once, with the most-recently-set value. | ||
`reflect` is called with one parameter, an object that has these properties: | ||
If `reflect` takes a `set` parameter, it may return a cleanup function that will be called immediately before the next `reflect` call. (Unlike its `derive` counterpart, `reflect`'s cleanup function is never called in response to unsubscriptions.) | ||
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](#regarding-subscription-less-writablederived-stores).) | ||
`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 `reflect` parameter is provided a function via an object with a `withOld` property, that function will be called with an additional `old` parameter after `reflecting`. This is the initial value of the origin stores, and will be an array if `origins` was an array. | ||
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: | ||
```javascript | ||
var coolStore = writableDerived(origins, derive, reflectExample, initial); | ||
function reflectExample({reflecting, old, set}) { | ||
// The set property was read in the process of destructuring. | ||
// Therefore, this function is guaranteed to be treated as asynchronous. | ||
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. | ||
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: Calling the `set` and `update` methods when the derived store has no subscriptions will subscribe to & then unsubscribe from all its origins. | ||
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 | ||
@@ -90,3 +69,3 @@ | ||
(json) => JSON.parse(json), | ||
({reflecting}) => JSON.stringify(reflecting) | ||
(object) => JSON.stringify(object) | ||
); | ||
@@ -108,6 +87,6 @@ console.log( Object.keys( get(objectStore) ) ); // ["I'm a property"] | ||
(object) => object["a horse"], | ||
({reflecting, old}) => { | ||
old["a horse"] = reflecting; | ||
return old; // needed to ensure objectStore.set is called with the correct value | ||
} | ||
{ withOld(reflecting, object) { | ||
object["a horse"] = reflecting; | ||
return object; // needed to ensure objectStore.set is called with the proper value | ||
} } | ||
); | ||
@@ -129,3 +108,3 @@ console.log( get(valueStore) ); // "a horse" | ||
([value1, value2]) => ( {"this is": value1, "it's": value2} ), | ||
({reflecting}) => [ reflecting["this is"], reflecting["it's"] ] | ||
(object) => [ object["this is"], object["it's"] ] | ||
); | ||
@@ -148,3 +127,3 @@ console.log( get(objectStore) ); // {"this is": "sparta", "it's": "monty python's flying circus"} | ||
(json) => JSON.parse(json), | ||
({reflecting}) => JSON.stringify(reflecting) | ||
(object) => JSON.stringify(object) | ||
); | ||
@@ -155,6 +134,6 @@ | ||
(hoard) => hoard["owner"], | ||
({reflecting, old}) => { | ||
old["owner"] = reflecting; | ||
return old; | ||
} | ||
{ withOld(reflecting, hoard) { | ||
hoard["owner"] = reflecting; | ||
return hoard; | ||
} } | ||
); | ||
@@ -164,6 +143,6 @@ var hoardContentsStore = writableDerived( | ||
(hoard) => hoard["possessions"], | ||
({reflecting, old}) => { | ||
old["possessions"] = reflecting; | ||
return old; | ||
} | ||
{ withOld(reflecting, hoard) { | ||
hoard["possessions"] = reflecting; | ||
return hoard; | ||
} } | ||
); | ||
@@ -175,9 +154,9 @@ | ||
return hoardContents.map( (item) => { | ||
return {item, owner: "hoarder"}; | ||
return {item, owner: hoarder}; | ||
}); | ||
}, | ||
({reflecting}) => { | ||
(itemList) => { | ||
// This is only for demonstration purposes, so we won't handle differing owners | ||
var hoarder = reflecting[0].owner; | ||
var hoardContents = reflecting.map( (itemListEntry) => { | ||
var hoarder = itemList[0].owner; | ||
var hoardContents = itemList.map( (itemListEntry) => { | ||
return itemListEntry["item"]; | ||
@@ -200,13 +179,7 @@ } ); | ||
/* | ||
reflect runs before subscribers. Since all our reflects are synchronous, | ||
before any subscribers can run, the next layer's set is called, which | ||
calls *its* reflect before *its* subscribers, and so on. This means | ||
stores will call their subscribers starting with the lowest/innermost | ||
layer and going up/out. | ||
Therefore, upon the update, the console logs: | ||
Upon the update, the console logs: | ||
[{item: "crown", owner: "protagonist"}, {item: "gold", owner: "protagonist"}] | ||
"protagonist" | ||
{owner: "protagonist", possessions: ["crown", "gold"]} | ||
"{\"owner\": \"protagonist\", \"possessions\": [\"crown\", \"gold\"]}" | ||
{owner: "protagonist", possessions: ["crown", "gold"]} | ||
"protagonist" | ||
[{item: "crown", owner: "protagonist"}, {item: "gold", owner: "protagonist"}] | ||
*/ | ||
@@ -213,0 +186,0 @@ ``` |
Sorry, the diff of this file is not supported yet
15066
77
195