reactive-box
Advanced tools
Comparing version 0.4.1 to 0.5.0
{ | ||
"name": "reactive-box", | ||
"version": "0.4.1", | ||
"version": "0.5.0", | ||
"description": "Minimalistic, fast, and highly efficient reactivity", | ||
@@ -49,3 +49,3 @@ "scripts": { | ||
"homepage": "https://github.com/betula/reactive-box#readme", | ||
"gitHead": "bb018e5ae252a95f3c16220536803a06b7afece9" | ||
"gitHead": "b72e1f05ff134c19d45f18e7cacd73d2afd6040e" | ||
} |
@@ -18,11 +18,15 @@ [![npm version](https://img.shields.io/npm/v/reactive-box?style=flat-square)](https://www.npmjs.com/package/reactive-box) [![bundle size](https://img.shields.io/bundlephobia/minzip/reactive-box?style=flat-square)](https://bundlephobia.com/result?p=reactive-box) [![code coverage](https://img.shields.io/coveralls/github/betula/reactive-box?style=flat-square)](https://coveralls.io/github/betula/reactive-box) [![typescript supported](https://img.shields.io/npm/types/typescript?style=flat-square)](./src/main.d.ts) | ||
```javascript | ||
import { box, sel, expr } import "reactive-box"; | ||
import { box, sel, expr } from "reactive-box"; | ||
const [get, set] = box(0); | ||
const [next] = sel(() => get() + 1); | ||
const [run, stop] = expr(() => { | ||
console.log(`Counter: ${get()} (next value: ${next()})`) | ||
}); | ||
run(); | ||
set(get() + 1); | ||
const [run, stop] = expr( | ||
() => `Counter: ${get()} (next value: ${next()})`, | ||
() => console.log(run()) | ||
); | ||
console.log(run()); // console: "Counter 0 (next value: 1)" | ||
set(get() + 1); // console: "Counter 1 (next value: 2)" | ||
``` | ||
@@ -32,2 +36,38 @@ | ||
It is a basis for full feature reactive mathematic! | ||
For example that possible syntax to transcript previous javascript code: | ||
``` | ||
a` = 0 // create reactive value | ||
next` = a` + 1 // create new reactive value, dependent on the previous one | ||
expr = { "Counter: ${a`} (next value: ${next`})" } // create reactive expression | ||
// subscribe to expression dependencies were change and run It again | ||
expr: () => console.log(expr()) | ||
// run the expression | ||
console.log(expr()) // message to console "Counter: 0 (next value: 1)" | ||
a` = a` + 1 // here will be fired log to console again with new "Counter: 1 (next value: 2)" message, because a` was changed. | ||
``` | ||
1. We create reactive `a` | ||
2. We create reactive operation `a + 1` | ||
3. We create reactive expression `"Counter: ${a} (next value: ${next})"` | ||
4. We subscribe to change of `a` and `next` reactive dependencies | ||
5. We run reactive expression | ||
6. We are increasing the value of reactive `a` for demonstration subscriber reaction | ||
These are three basic elements necessary for creating data flow any difficulty. | ||
The first element is a reactive container for an immutable value. All reactions beginning from container change value reaction. | ||
The second one is the middle element. It uses all reactive containers as a source of values and returns the result of the expression. It's a transformer set of reactive values to a single one. The selector can be used as a reactive container in other selectors and expressions. | ||
It subscribes to change in any of the dependencies. And will recalculate the value if some of the dependency changed. | ||
And the last one is a reaction subscriber. It provides the possibility to subscribe to change any set of reactive containers. It can be run again after the listener was called. | ||
Below we will talk about more high level abstraction, to the world of React and integration reactive-box into, for best possibilities together! | ||
Basic usage examples: | ||
@@ -122,2 +162,2 @@ | ||
Cheers and happy coding! 👋 | ||
Thank you for your time! |
115
src/main.js
/** | ||
* 0: rels or (sync for expr) | ||
* 1: deps | ||
* 2: (valid for sel) | ||
* 3: (recalc for sel) | ||
* 2: level | ||
* 3: (valid for sel) | ||
* 4: (recalc for sel) | ||
*/ | ||
let context_node; | ||
let active_bound; | ||
let write_phase; | ||
let level_nodes = new Map(); | ||
let levels = new Set(); | ||
@@ -22,2 +25,7 @@ // node: sel or expr node | ||
node[0].add(context_node); | ||
// calculate level | ||
if (context_node[2] < node[2] + 1) { | ||
context_node[2] = node[2] + 1; | ||
} | ||
} | ||
@@ -27,36 +35,64 @@ }; | ||
const write = (box_node) => { | ||
if (active_bound) box_node[0].forEach((rel) => active_bound.add(rel)); | ||
else { | ||
const exprs = new Set(); | ||
const sels = new Set(); | ||
let limit = 10000; | ||
box_node[0].forEach((rel) => { | ||
const level = rel[2]; | ||
active_bound = new Set(box_node[0]); | ||
let list = level_nodes.get(level); | ||
!list && level_nodes.set(level, list = new Set()); | ||
list.add(rel); | ||
levels.add(level); | ||
}); | ||
if (!write_phase) { | ||
write_phase = 1; | ||
try { | ||
while (active_bound.size) { | ||
active_bound.forEach((node) => { | ||
if (node.length === 2) exprs.add(node); | ||
let limit = 10000; | ||
const exprs = new Set(); | ||
while (levels.size) { | ||
const levels_iter = levels.values(); | ||
let current = levels_iter.next().value; | ||
let level; | ||
while ((level = levels_iter.next().value)) { | ||
if (level < current) current = level; | ||
} | ||
const nodes = level_nodes.get(current); | ||
levels.delete(current); | ||
level_nodes.delete(current); | ||
const sels = []; | ||
nodes.forEach((node) => { | ||
if (node.length === 3) exprs.add(node); | ||
else { | ||
if (node[0].size) sels.add(node); | ||
else node[2] = 0; | ||
if (node[0].size) sels.push(node); | ||
else node[3] = 0; | ||
} | ||
free(node, 1); | ||
}); | ||
active_bound.clear(); | ||
sels.forEach((sel_node) => { | ||
if (sel_node[3]()) { | ||
sel_node[0].forEach((rel) => active_bound.add(rel)); | ||
if (sel_node[4]()) { | ||
write(sel_node); | ||
free(sel_node, 0); | ||
} | ||
}); | ||
sels.clear(); | ||
if (!active_bound.size) { | ||
const iter = exprs.values(); | ||
let expr_node; | ||
while ((expr_node = iter.next().value)) { | ||
expr_node[0](); | ||
exprs.delete(expr_node); | ||
if (active_bound.size) break; | ||
const expr_iter = exprs.values(); | ||
let expr_node; | ||
expr_loop: | ||
while ((expr_node = expr_iter.next().value)) { | ||
expr_node[0](); | ||
exprs.delete(expr_node); | ||
const levels_iter = levels.values(); | ||
let level; | ||
while ((level = levels_iter.next().value)) { | ||
if (level < current) { | ||
break expr_loop; | ||
} | ||
} | ||
@@ -67,5 +103,8 @@ } | ||
} | ||
} finally { | ||
active_bound = 0; | ||
} | ||
finally { | ||
write_phase = 0; | ||
level_nodes.clear(); | ||
levels.clear(); | ||
} | ||
} | ||
@@ -75,3 +114,4 @@ }; | ||
const box = (value, change_listener, comparer = Object.is) => { | ||
const box_node = [new Set()]; | ||
// rels, _, level | ||
const box_node = [new Set(), 0, 0]; | ||
return [ | ||
@@ -103,2 +143,3 @@ () => (read(box_node), value), | ||
context_node = sel_node; | ||
context_node[2] = 0; // clear level | ||
try { | ||
@@ -110,3 +151,4 @@ return body.call(last_context); | ||
} | ||
const sel_node = [new Set(), new Set(), 0, () => { | ||
// rels, deps, level, is_cached, checker | ||
const sel_node = [new Set(), new Set(), 0, 0, () => { | ||
let next = run(); | ||
@@ -119,8 +161,8 @@ return comparer(cache, next) | ||
function () { | ||
read(sel_node); | ||
last_context = this; | ||
if (!sel_node[2]) { | ||
if (!sel_node[3]) { | ||
cache = run(); | ||
sel_node[2] = 1; | ||
sel_node[3] = 1; | ||
} | ||
read(sel_node); | ||
return cache; | ||
@@ -131,3 +173,3 @@ }, | ||
free(sel_node, 0); | ||
sel_node[2] = cache = 0; | ||
sel_node[3] = cache = 0; | ||
last_context = null; | ||
@@ -141,3 +183,6 @@ }, | ||
if (!sync) sync = () => run.call(last_context); | ||
const expr_node = [sync, new Set()]; | ||
// sync, deps, level | ||
const expr_node = [sync, new Set(), 0]; | ||
function run() { | ||
@@ -149,2 +194,3 @@ let result; | ||
context_node = expr_node; | ||
context_node[2] = 0; // clear level | ||
try { | ||
@@ -157,2 +203,3 @@ result = body.apply((last_context = this), arguments); | ||
} | ||
return [ | ||
@@ -159,0 +206,0 @@ run, |
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
13741
192
161