Skip to content

set-state/map

Repository files navigation

@set-state/map

npm version Build Status Coverage Status License: MIT Codestyle Prettier Standard Version conduct

.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.

Getting Started

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)

How to use it

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
}
*/