Add documentation for frptools.
This commit is contained in:
parent
4e70d07f71
commit
d5c1cd852b
79
README.md
Normal file
79
README.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# FRP tools
|
||||||
|
|
||||||
|
Observer and computed value stores designed to work together for storing real state and derived
|
||||||
|
state.
|
||||||
|
|
||||||
|
# [observable](./src/observable.js)
|
||||||
|
|
||||||
|
`observable` is a simple value store that can report when its value changes. It is good for wrapping
|
||||||
|
external props passed into a component so compute types can dependent on them.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Creation
|
||||||
|
|
||||||
|
Creates and sets initial value to `true`
|
||||||
|
|
||||||
|
```js
|
||||||
|
const inViewport = observable(true);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read
|
||||||
|
|
||||||
|
Call it to receive the stored value.
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (inViewport()) {
|
||||||
|
/* inViewport is truthy */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Change
|
||||||
|
|
||||||
|
Call it passing the new value. If any computed stores depend on this value they will be marked dirty
|
||||||
|
and re-evaluated the next time they are read from.
|
||||||
|
|
||||||
|
```js
|
||||||
|
inViewport(false);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subscribe to changes
|
||||||
|
|
||||||
|
Call the subscribe method with a callback that will be called when the observable is changed to a
|
||||||
|
different value.
|
||||||
|
|
||||||
|
```js
|
||||||
|
inViewport.subscribe(console.log.bind(console));
|
||||||
|
```
|
||||||
|
|
||||||
|
# [computed](./src/computed.js)
|
||||||
|
|
||||||
|
`computed` is a functional store that depends on the values of observables or other computeds. They
|
||||||
|
derive value from observables rather than store value and hence cannot be set directly.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
A `computed` will subscribe to its dependencies in such a way that it will be marked as _dirty_ when
|
||||||
|
any dependency changes. Whenever it is read from, if will recompute if it is dirty, otherwise it
|
||||||
|
just return the stored result from the last time it computed.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Creation
|
||||||
|
|
||||||
|
```js
|
||||||
|
const showDialog = computed(
|
||||||
|
(inVP, shouldShow) => inVP && shouldShow, // computation function
|
||||||
|
[inViewport, shouldShow] // array of dependencies, can be either observable or computed
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (showDialog()) {
|
||||||
|
/* showDialog() is truthy */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Call it to receive the stored value, recomputing if necessary.
|
||||||
51
src/computed.js
Normal file
51
src/computed.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
export function computed(fn, dependencies = []) {
|
||||||
|
const subscribers = new Set();
|
||||||
|
const dependents = new Set();
|
||||||
|
let val = undefined;
|
||||||
|
let isDirty = true;
|
||||||
|
|
||||||
|
function _computedDirtyReporter() {
|
||||||
|
if (!isDirty) {
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
|
dependents.forEach(runParam);
|
||||||
|
|
||||||
|
if (subscribers.size) {
|
||||||
|
accessor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dependentSubscriptions = Array.from(dependencies).map(d => d._d(_computedDirtyReporter));
|
||||||
|
|
||||||
|
const accessor = function _computed() {
|
||||||
|
if (isDirty) {
|
||||||
|
const newVal = fn.apply(null, dependencies.map(runParam));
|
||||||
|
isDirty = false;
|
||||||
|
if (newVal !== val) {
|
||||||
|
val = newVal;
|
||||||
|
subscribers.forEach(s => s(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
accessor.subscribe = fn => {
|
||||||
|
subscribers.add(fn);
|
||||||
|
return () => subscribers.delete(fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
accessor._d = fn => {
|
||||||
|
dependents.add(fn);
|
||||||
|
return () => dependents.delete(fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
accessor.detach = () => {
|
||||||
|
subscribers.clear();
|
||||||
|
dependents.clear();
|
||||||
|
dependentSubscriptions.forEach(runParam);
|
||||||
|
};
|
||||||
|
|
||||||
|
return accessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const runParam = a => a();
|
||||||
111
src/index.js
111
src/index.js
@ -1,109 +1,2 @@
|
|||||||
// observable is a simple value store that can report when its value changes.
|
export { observable } from './observable';
|
||||||
// It is good for wrapping external props passed into a component so compute
|
export { computed } from './computed';
|
||||||
// types can dependent on them.
|
|
||||||
// Usage:
|
|
||||||
//
|
|
||||||
// Creation:
|
|
||||||
// `const inViewport = observable(true);`
|
|
||||||
// Creates and sets initial value to `true`
|
|
||||||
//
|
|
||||||
// Read:
|
|
||||||
// `if (inViewport()) { }`
|
|
||||||
// Call it to receive the stored value.
|
|
||||||
//
|
|
||||||
// Change:
|
|
||||||
// `inViewport(false);`
|
|
||||||
// Call it passing the new value. If any computed stores depend on this value
|
|
||||||
// they will be marked dirty and re-evaluated the next time they are read from.
|
|
||||||
//
|
|
||||||
// Subscribe to changes:
|
|
||||||
// `inViewport.subscribe(console.log.bind(console))`
|
|
||||||
// Call the subscribe method with a callback that will be called when the
|
|
||||||
// observable is changed to a different value.
|
|
||||||
|
|
||||||
export function observable(store) {
|
|
||||||
const subscribers = new Set();
|
|
||||||
|
|
||||||
const accessor = function _observable(newVal) {
|
|
||||||
if (newVal !== undefined && store !== newVal) {
|
|
||||||
store = newVal;
|
|
||||||
subscribers.forEach(s => s(store));
|
|
||||||
}
|
|
||||||
return store;
|
|
||||||
};
|
|
||||||
|
|
||||||
accessor.subscribe = accessor._d = fn => {
|
|
||||||
subscribers.add(fn);
|
|
||||||
return () => subscribers.delete(fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
accessor.unsubscribeAll = () => subscribers.clear();
|
|
||||||
|
|
||||||
return accessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// computed is a functional store that depends on the values of observables or other computeds. They cannot be set directly.
|
|
||||||
//
|
|
||||||
// Behavior:
|
|
||||||
// computed will subscribe to its dependencies in such a way that it will be marked as "dirty" when any dependency changes.
|
|
||||||
//
|
|
||||||
// Usage:
|
|
||||||
//
|
|
||||||
// Creation:
|
|
||||||
// const showDialog = computed((inVP, shouldShow) => (inVP && shouldShow), [inViewport, shouldShow]);
|
|
||||||
//
|
|
||||||
// Read:
|
|
||||||
// `if (showDialog()) { alert("Hi"); }`
|
|
||||||
// Call it to receive the stored value.
|
|
||||||
|
|
||||||
export function computed(fn, dependencies = []) {
|
|
||||||
const subscribers = new Set();
|
|
||||||
const dependents = new Set();
|
|
||||||
let val = undefined;
|
|
||||||
let isDirty = true;
|
|
||||||
|
|
||||||
function _computedDirtyReporter() {
|
|
||||||
if (!isDirty) {
|
|
||||||
isDirty = true;
|
|
||||||
}
|
|
||||||
dependents.forEach(runParam);
|
|
||||||
|
|
||||||
if (subscribers.size) {
|
|
||||||
accessor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dependentSubscriptions = Array.from(dependencies).map(d => d._d(_computedDirtyReporter));
|
|
||||||
|
|
||||||
const accessor = function _computed() {
|
|
||||||
if (isDirty) {
|
|
||||||
const newVal = fn.apply(null, dependencies.map(runParam));
|
|
||||||
isDirty = false;
|
|
||||||
if (newVal !== val) {
|
|
||||||
val = newVal;
|
|
||||||
subscribers.forEach(s => s(val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
};
|
|
||||||
|
|
||||||
accessor.subscribe = fn => {
|
|
||||||
subscribers.add(fn);
|
|
||||||
return () => subscribers.delete(fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
accessor._d = fn => {
|
|
||||||
dependents.add(fn);
|
|
||||||
return () => dependents.delete(fn);
|
|
||||||
};
|
|
||||||
|
|
||||||
accessor.detach = () => {
|
|
||||||
subscribers.clear();
|
|
||||||
dependents.clear();
|
|
||||||
dependentSubscriptions.forEach(runParam);
|
|
||||||
};
|
|
||||||
|
|
||||||
return accessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
const runParam = a => a();
|
|
||||||
|
|||||||
20
src/observable.js
Normal file
20
src/observable.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export function observable(store) {
|
||||||
|
const subscribers = new Set();
|
||||||
|
|
||||||
|
const accessor = function _observable(newVal) {
|
||||||
|
if (newVal !== undefined && store !== newVal) {
|
||||||
|
store = newVal;
|
||||||
|
subscribers.forEach(s => s(store));
|
||||||
|
}
|
||||||
|
return store;
|
||||||
|
};
|
||||||
|
|
||||||
|
accessor.subscribe = accessor._d = fn => {
|
||||||
|
subscribers.add(fn);
|
||||||
|
return () => subscribers.delete(fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
accessor.unsubscribeAll = () => subscribers.clear();
|
||||||
|
|
||||||
|
return accessor;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user