.map()
plugin for @set-state
By Paul Grenier (@AutoSponge)
@set-state/map
plugs into a factory or state function. Any node created will have the .map()
function.
npm install --save @set-state/core @set-state/map
// import factory, {state} from '@set-state/core'
// import mapPlugin from '@set-state/map'
state.use(mapPlugin)
Calling node.map(predicate)
creates a "sealed" node
based on the return value of the predicate function. The predicate recieves the node's value as a parameter.
Mapped nodes update synchronously if their predicate returns a non-Promise value.
const str = state('beep')
const bang = str.map(val => `${val}!`)
bang() // => 'beep!'
bang('foo') // bang is sealed, so this is ignored. Safety first!
bang() // => 'beep!'
Calling node.map(predicate, errorHandler)
creates a "sealed" node
that will update its value when the predicate resolves. If the predicate rejects or throws, the errorHandler will be called passing the error as a parameter.
This example demonstrates how you can use .map
asynchronously.
const selectedItem = state()
const selectedItemData = selectedItem.map(id => {
if (!id) return {msg: 'select an item'} // sync value
return fetch(`/api/items/${id}`) // async value
.then(res => res.json())
}, renderError)
selectedItemData.on(render)
Note:
.map()
has two additional features for async predicates to prevent memory leaks, update storms, and out-of-sync state.
First, similar to a "debounce", mapped nodes will not invoke the predicate while awaiting a new value from a previous invocation.
Second, when the mapped node updates value, if the cached node.value
differs from the current node.value
, it will call the predicate again with the latest value attempting to "fast forward" to the correct state.
const a = state(0)
const b = a.map(n => n + 1)
b
/*
{ [Function: f]
[...omitted]
locals: {
map: { <== map's cache object
value: 0, <== value of a used to invoke the predicate
awating: false <== update was synchronous
}
},
map: [Function], <== the map method
value: 1 <== current value of b
}
*/