Comparing version 0.3.3 to 0.3.4
# Changelog | ||
## 0.3.4 - 2019-01-04 | ||
- Added optional comparator for signals. | ||
- Removed redundant type checks and extra function calls. | ||
- Changed S.js to a dependency instead of a peer dependency. | ||
## 0.3.2 - 2018-12-30 | ||
@@ -4,0 +10,0 @@ - Separated useSignal getter/setters for clearer more consistent API |
@@ -13,17 +13,8 @@ import S$1 from 's-js'; | ||
function clone(v) { | ||
if (!isObject(v)) return v; | ||
if (Array.isArray(v)) return v.slice(0); | ||
return Object.assign({}, v); | ||
} | ||
function isWrappable(obj) { return obj !== null && typeof obj === 'object' && !(obj instanceof Element); } | ||
function isObject(obj) { | ||
let ref; | ||
return obj !== null && ((ref = typeof obj) === 'object' || ref === 'function'); | ||
} | ||
function diff(a, b, path = []) { | ||
let i, k, l, len, v; | ||
const r = []; | ||
if (!isObject(a) || (b == null)) { | ||
if (!isWrappable(a) || (b == null)) { | ||
if (a !== b) { | ||
@@ -61,7 +52,7 @@ r.push(path.concat([a])); | ||
let result, unwrapped, v; | ||
if (result = item != null ? item._state : void 0) return result; | ||
if (!isObject(item) || (typeof item === 'function') || (item instanceof Element)) return item; | ||
if (Object.isFrozen(item)) item = clone(item); | ||
if (result = (item != null) && item._state) return result; | ||
if (!isWrappable(item)) return item; | ||
if (Array.isArray(item)) { | ||
if (Object.isFrozen(item)) item = item.slice(0); | ||
for (let i = 0, l = item.length; i < l; i++) { | ||
@@ -72,2 +63,3 @@ v = item[i]; | ||
} else { | ||
if (Object.isFrozen(item)) item = Object.assign({}, item); | ||
let keys = Object.keys(item); | ||
@@ -110,5 +102,15 @@ for (let i = 0, l = keys.length; i < l; i++) { | ||
if (property === '_state') return target; | ||
const value = target[property]; | ||
if (S$1.isListening() && typeof value !== 'function') track(target, property, value); | ||
return (!isObject(value) || typeof value === 'function' || value instanceof Element) ? value : wrap(value); | ||
const value = target[property], | ||
wrappable = isWrappable(value); | ||
if (S$1.isListening() && typeof value !== 'function') { | ||
let nodes, node; | ||
if (wrappable && (nodes = getDataNodes(value))) { | ||
node = nodes._self || (nodes._self = S$1.makeDataNode()); | ||
node.current(); | ||
} | ||
nodes = getDataNodes(target); | ||
node = nodes[property] || (nodes[property] = S$1.makeDataNode()); | ||
node.current(); | ||
} | ||
return wrappable ? wrap(value) : value; | ||
}, | ||
@@ -123,26 +125,8 @@ | ||
function getDataNode(target) { | ||
let node = target[SNODE]; | ||
if (!node) target[SNODE] = node = {}; | ||
return node; | ||
function getDataNodes(target) { | ||
let nodes = target[SNODE]; | ||
if (!nodes) target[SNODE] = nodes = {}; | ||
return nodes; | ||
} | ||
function track(target, property, value) { | ||
let node; | ||
if (isObject(value) && !(value instanceof Element)) { | ||
if (node = getDataNode(value)) { | ||
if (!node._self) node._self = S$1.makeDataNode(); | ||
node._self.current(); | ||
} | ||
} | ||
node = getDataNode(target); | ||
node[property] || (node[property] = S$1.makeDataNode()); | ||
node[property].current(); | ||
} | ||
function trigger(node, property, notify) { | ||
if (node[property]) node[property].next(); | ||
if (notify && node._self) node._self.next(); | ||
} | ||
function setProperty(state, property, value) { | ||
@@ -157,3 +141,5 @@ value = unwrap(value); | ||
else state[property] = value; | ||
trigger(getDataNode(state), property, notify); | ||
let nodes = getDataNodes(state), node; | ||
(node = nodes[property]) && node.next(); | ||
notify && (node = nodes._self) && node.next(); | ||
} | ||
@@ -207,5 +193,7 @@ | ||
let value = path[0]; | ||
if (typeof value === 'function') | ||
value = value(typeof current[part] === 'object' ? wrap(current[part]) : current[part], traversed.concat([part])); | ||
if (!replace && current[part] != null && typeof current[part] === 'object' && value !== null && typeof value === 'object' && !Array.isArray(value)) | ||
if (typeof value === 'function') { | ||
const currentPart = current[part]; | ||
value = value(isWrappable(currentPart) ? wrap(currentPart) : currentPart, traversed.concat([part])); | ||
} | ||
if (!replace && isWrappable(current[part]) && isWrappable(value) && !Array.isArray(value)) | ||
return mergeState(current[part], value); | ||
@@ -242,11 +230,25 @@ return setProperty(current, part, value); | ||
function useMemo(fn, seed) { return S$1(fn, seed); } | ||
function useSignal(value) { | ||
function useSignal(value, comparator) { | ||
const d = S$1.makeDataNode(value); | ||
return [d.current.bind(d), d.next.bind(d)]; | ||
let setter; | ||
if (comparator) { | ||
let age = -1; | ||
setter = v => { | ||
if (!comparator(value, v)) { | ||
const time = d.clock().time(); | ||
if (time === age) | ||
throw new Error(`Conflicting value update: ${v} is not the same as ${value}`); | ||
age = time; | ||
value = v; | ||
d.next(v); | ||
} | ||
}; | ||
} else setter = d.next.bind(d); | ||
return [d.current.bind(d), setter]; | ||
} | ||
function useMemo(fn, seed) { return S$1(fn, seed); } | ||
function useEffect(fn, deps, defer) { | ||
if (!deps) return S$1.effect(fn); | ||
if (!deps) return S$1.makeComputationNode(fn); | ||
S$1.on(deps, fn, undefined, defer); | ||
@@ -257,2 +259,2 @@ } | ||
export { root, useCleanup, sample, freeze, unwrap, observable, useState, reconcile, useMemo, useSignal, useEffect }; | ||
export { root, useCleanup, sample, freeze, unwrap, observable, useState, reconcile, useSignal, useMemo, useEffect }; |
@@ -15,5 +15,5 @@ # API | ||
### `useSignal(initialValue): [getValueFn, setValueFn]` | ||
### `useSignal(initialValue, comparatorFn): [getValueFn, setValueFn]` | ||
Creates a new signal that can be used for reactive tracking. | ||
Creates a new signal that can be used for reactive tracking. By default signals always notify on setting a value. However a comparator can be passed in to indicate whether the values should be considered equal and listeners not notified. | ||
@@ -20,0 +20,0 @@ ### `useMemo(prev => <code>, initialValue): getValueFn` |
@@ -10,3 +10,3 @@ # Signals | ||
function fromInterval(delay) { | ||
function useTick(delay) { | ||
const [getCount, setCount] = useSignal(0); | ||
@@ -70,7 +70,9 @@ handle = setInterval(() => setCount(getCount() + 1), delay); | ||
const [getSeconds, setSeconds] = useSignal(0); | ||
const div = <div>Number of seconds elapsed: {( getSeconds() )}</div> | ||
root(() => { | ||
const [getSeconds, setSeconds] = useSignal(0); | ||
div = <div>Number of seconds elapsed: {( getSeconds() )}</div> | ||
setInterval(() => setSeconds(getSeconds() + 1), 1000) | ||
root(() => document.body.appendChild(div)) | ||
setInterval(() => setSeconds(getSeconds() + 1), 1000) | ||
document.body.appendChild(div) | ||
}) | ||
``` | ||
@@ -77,0 +79,0 @@ |
100
lib/solid.js
@@ -19,17 +19,8 @@ 'use strict'; | ||
function clone(v) { | ||
if (!isObject(v)) return v; | ||
if (Array.isArray(v)) return v.slice(0); | ||
return Object.assign({}, v); | ||
} | ||
function isWrappable(obj) { return obj !== null && typeof obj === 'object' && !(obj instanceof Element); } | ||
function isObject(obj) { | ||
let ref; | ||
return obj !== null && ((ref = typeof obj) === 'object' || ref === 'function'); | ||
} | ||
function diff(a, b, path = []) { | ||
let i, k, l, len, v; | ||
const r = []; | ||
if (!isObject(a) || (b == null)) { | ||
if (!isWrappable(a) || (b == null)) { | ||
if (a !== b) { | ||
@@ -67,7 +58,7 @@ r.push(path.concat([a])); | ||
let result, unwrapped, v; | ||
if (result = item != null ? item._state : void 0) return result; | ||
if (!isObject(item) || (typeof item === 'function') || (item instanceof Element)) return item; | ||
if (Object.isFrozen(item)) item = clone(item); | ||
if (result = (item != null) && item._state) return result; | ||
if (!isWrappable(item)) return item; | ||
if (Array.isArray(item)) { | ||
if (Object.isFrozen(item)) item = item.slice(0); | ||
for (let i = 0, l = item.length; i < l; i++) { | ||
@@ -78,2 +69,3 @@ v = item[i]; | ||
} else { | ||
if (Object.isFrozen(item)) item = Object.assign({}, item); | ||
let keys = Object.keys(item); | ||
@@ -116,5 +108,15 @@ for (let i = 0, l = keys.length; i < l; i++) { | ||
if (property === '_state') return target; | ||
const value = target[property]; | ||
if (S$1.isListening() && typeof value !== 'function') track(target, property, value); | ||
return (!isObject(value) || typeof value === 'function' || value instanceof Element) ? value : wrap(value); | ||
const value = target[property], | ||
wrappable = isWrappable(value); | ||
if (S$1.isListening() && typeof value !== 'function') { | ||
let nodes, node; | ||
if (wrappable && (nodes = getDataNodes(value))) { | ||
node = nodes._self || (nodes._self = S$1.makeDataNode()); | ||
node.current(); | ||
} | ||
nodes = getDataNodes(target); | ||
node = nodes[property] || (nodes[property] = S$1.makeDataNode()); | ||
node.current(); | ||
} | ||
return wrappable ? wrap(value) : value; | ||
}, | ||
@@ -129,26 +131,8 @@ | ||
function getDataNode(target) { | ||
let node = target[SNODE]; | ||
if (!node) target[SNODE] = node = {}; | ||
return node; | ||
function getDataNodes(target) { | ||
let nodes = target[SNODE]; | ||
if (!nodes) target[SNODE] = nodes = {}; | ||
return nodes; | ||
} | ||
function track(target, property, value) { | ||
let node; | ||
if (isObject(value) && !(value instanceof Element)) { | ||
if (node = getDataNode(value)) { | ||
if (!node._self) node._self = S$1.makeDataNode(); | ||
node._self.current(); | ||
} | ||
} | ||
node = getDataNode(target); | ||
node[property] || (node[property] = S$1.makeDataNode()); | ||
node[property].current(); | ||
} | ||
function trigger(node, property, notify) { | ||
if (node[property]) node[property].next(); | ||
if (notify && node._self) node._self.next(); | ||
} | ||
function setProperty(state, property, value) { | ||
@@ -163,3 +147,5 @@ value = unwrap(value); | ||
else state[property] = value; | ||
trigger(getDataNode(state), property, notify); | ||
let nodes = getDataNodes(state), node; | ||
(node = nodes[property]) && node.next(); | ||
notify && (node = nodes._self) && node.next(); | ||
} | ||
@@ -213,5 +199,7 @@ | ||
let value = path[0]; | ||
if (typeof value === 'function') | ||
value = value(typeof current[part] === 'object' ? wrap(current[part]) : current[part], traversed.concat([part])); | ||
if (!replace && current[part] != null && typeof current[part] === 'object' && value !== null && typeof value === 'object' && !Array.isArray(value)) | ||
if (typeof value === 'function') { | ||
const currentPart = current[part]; | ||
value = value(isWrappable(currentPart) ? wrap(currentPart) : currentPart, traversed.concat([part])); | ||
} | ||
if (!replace && isWrappable(current[part]) && isWrappable(value) && !Array.isArray(value)) | ||
return mergeState(current[part], value); | ||
@@ -248,11 +236,25 @@ return setProperty(current, part, value); | ||
function useMemo(fn, seed) { return S$1(fn, seed); } | ||
function useSignal(value) { | ||
function useSignal(value, comparator) { | ||
const d = S$1.makeDataNode(value); | ||
return [d.current.bind(d), d.next.bind(d)]; | ||
let setter; | ||
if (comparator) { | ||
let age = -1; | ||
setter = v => { | ||
if (!comparator(value, v)) { | ||
const time = d.clock().time(); | ||
if (time === age) | ||
throw new Error(`Conflicting value update: ${v} is not the same as ${value}`); | ||
age = time; | ||
value = v; | ||
d.next(v); | ||
} | ||
}; | ||
} else setter = d.next.bind(d); | ||
return [d.current.bind(d), setter]; | ||
} | ||
function useMemo(fn, seed) { return S$1(fn, seed); } | ||
function useEffect(fn, deps, defer) { | ||
if (!deps) return S$1.effect(fn); | ||
if (!deps) return S$1.makeComputationNode(fn); | ||
S$1.on(deps, fn, undefined, defer); | ||
@@ -271,4 +273,4 @@ } | ||
exports.reconcile = reconcile; | ||
exports.useSignal = useSignal; | ||
exports.useMemo = useMemo; | ||
exports.useSignal = useSignal; | ||
exports.useEffect = useEffect; |
{ | ||
"name": "solid-js", | ||
"description": "A declarative JavaScript library for building user interfaces.", | ||
"version": "0.3.3", | ||
"version": "0.3.4", | ||
"author": "Ryan Carniato", | ||
@@ -19,12 +19,13 @@ "license": "MIT", | ||
}, | ||
"peerDependencies": { | ||
"babel-plugin-jsx-dom-expressions": "~0.3.0", | ||
"dependencies": { | ||
"s-js": "~0.4.9" | ||
}, | ||
"peerDependencies": { | ||
"babel-plugin-jsx-dom-expressions": "~0.3.0" | ||
}, | ||
"devDependencies": { | ||
"jest": "~23.6.0", | ||
"rollup": "^1.0.0", | ||
"rollup-plugin-node-resolve": "^4.0.0", | ||
"s-js": "~0.4.9" | ||
"rollup-plugin-node-resolve": "^4.0.0" | ||
} | ||
} |
@@ -25,3 +25,3 @@ # Solid.js | ||
<h1>Welcome</h1> | ||
<p>Hello {props.name}</p> | ||
<p>Hello {(props.name)}</p> | ||
</> | ||
@@ -35,3 +35,3 @@ | ||
```sh | ||
> npm install solid-js s-js babel-plugin-jsx-dom-expressions | ||
> npm install solid-js babel-plugin-jsx-dom-expressions | ||
``` | ||
@@ -53,3 +53,3 @@ | ||
return <div>{state.counter}</div> | ||
return <div>{(state.counter)}</div> | ||
} | ||
@@ -91,10 +91,8 @@ ``` | ||
This is also primary mechanism to interopt with store technologies like Redux, Apollo, RxJS which expose themselves as Observables or Promises. When you hook up these effects you can use standard methods to map the properties you want and the reconcile method will diff the changes to only affect the minimal amount. | ||
Solid State also exposes a reconcile method used with setState that does immutable diffing to allow for automatic efficient interopt with store technologies like Redux, Apollo, RxJS which expose themselves as Observables or Promises. | ||
```js | ||
useEffect(() => { | ||
const disposable = store.observable() | ||
.subscribe(({ todos }) => setState(reconcile('todos', todos))); | ||
useCleanup(() => disposable.unsubscribe()); | ||
}); | ||
const disposable = store.observable() | ||
.subscribe(({ todos }) => setState(reconcile('todos', todos))); | ||
useCleanup(() => disposable.unsubscribe()); | ||
``` | ||
@@ -179,2 +177,10 @@ | ||
I cover this in more detail in my Bring Your Own Framework Blog Series. | ||
## Blog Series | ||
* [Part 1: Writing a JS Framework in 2018](https://medium.com/@ryansolid/b-y-o-f-part-1-writing-a-js-framework-in-2018-b02a41026929) | ||
* [Part 2: Web Components as Containers](https://medium.com/@ryansolid/b-y-o-f-part-2-web-components-as-containers-85e04a7d96e9) | ||
* [Part 3: Change Management in JavaScript Frameworks](https://medium.com/@ryansolid/b-y-o-f-part-3-change-management-in-javascript-frameworks-6af6e436f63c) | ||
* Part 4 & 5: <i>Coming Soon</i> | ||
## Documentation | ||
@@ -181,0 +187,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
47143
3
642
208
+ Addeds-js@~0.4.9