@develephant/props-aware
v1.2.1
Published
A "living" global properties data store. Dispatch with ease.
Downloads
4
Maintainers
Readme
PropsAware
Properties as dispatchers. A "living" global properties data store.
Install
npm i @develephant/props-aware --save
Usage
const PropsAware = require('@develephant/props-aware')
Overview
PropsAware (or PA
) is a global property object that emits events on property changes.
You can listen for these changes virtually anywhere in your program.
Events are only emitted when the underlying property data changes.
In code it looks like this:
//file-one.js
/* Pull in PropsAware as PA */
const PA = require('@develephant/props-aware')
let props = PA.props()
PA.on('score', (val) => {
console.log(val)
})
props.score = 300
/* outputs 300 */
Lets create another JS file, and add the following:
//file-two.js
/* Pull in PropsAware as PA */
const PA = require('@develephant/props-aware')
props = PA.props()
props.score = 500 //will trigger an update
/* file-one.js will output 500 */
props.score = 500 //will not trigger an update, same data
Discussion
Some things to consider before, or when using PropsAware:
- Only properties are managed; strings, numbers, booleans, arrays, and "data" objects.
- Properties are stored as a flat tree. Only root keys trigger update events. [1]
- When you set a
PA
property, its immediately available to all listeners. - Callbacks are set on the
PropsAware
(PA
) object directly, not the property itself. [2] - To change a property without emitting an update event, use
PA.set(p, v)
. - Limit yourself to a handful of base
PA
properties, and pass primitives for flow control. - There are no guarantees on delivery order or timing. it will get there though.
- When creating object instances with
PA
properties, each instance will have its own listener. [3] - Dont set
PA
properties in anonAll
handler. [4]
Footnotes
1) Only root keys trigger an update:
//example PA props object
...
let props = {
score: 200
user: {
name: 'User',
color: 'green'
}
}
In the object above, when the score
property is updated with a new value, an event will be emitted.
The user
key holds an object. When the key is set with an updated object, the user
key will trigger:
//only root keys emit
PA.on('user', (val) => {
console.log(val.color) //blue
})
props.user = { color: 'blue' }
Changing user.color
directly will not trigger an event. Additionally, in the code above, the name
key will be stripped away. This may be fine if thats whats intended.
An easy way to handle this, is pulling the object first:
let obj = props.user //by ref
obj.color = 'yellow'
props.user = obj //will trigger
//yellow
Or, if you want to be super fancy...
let obj = Object.assign({}, props.user) //cloned
obj.color = 'yellow'
props.user = obj //will trigger
The above holds true for Arrays too.
2) Callbacks are set on the PA
object, not the property:
...
//works!
PA.on('score', (val) => {
console.log(val)
})
//does NOT work
score.on((val) => {
console.log(val)
}
3) Created instances of a Class will all emit:
class MyClass {
constructor() {
this.props = PA.props()
PA.on('score', (val) => {
console.log(val)
})
}
}
const instance1 = new MyClass()
const instance2 = new MyClass()
const instance3 = new MyClass()
instance1.props.score = 100
/* instance1 outputs 100 */
/* instance2 outputs 100 */
/* instance3 outputs 100 */
4) Dont set properties in an onAll
handler:
let props = PA.props()
PA.onAll((val, prop) => { //infinite loop
props[prop] = val //dont do it!
props.dontsetme = 'oops' //think of the puppies!!
})
If you really, really need/want to set a PA
property in an onAll
handler you can either:
Set it silently, without triggering an update
let props = PA.props()
PA.onAll((val, prop) => {
PA.set(prop, val) //does not emit
})
Or, you can use this workaround/hack:
let props = PA.props()
PA.onAll((val, prop) => {
setTimeout(() => { props[prop] = val }, 1)
//will run until your computer starts smoking
})
But, at the end of the day, thats an infinite loop. Its not advisable.
Keep it simple
Try to keep the amount of PropsAware properties to a minimum. Pass strings, numbers, and booleans to handle messaging and state.
Consider the following:
const PA = require('@develephant/props-aware')
let props = PA.props()
//this is okay...
props.walking = true
props.running = false
//but this is better!
props.pace = 'walking'
//OR
props.pace = 'running'
In most basic programs, you shouldnt need more than 4-5
PA
properties.
API
props() -> PropsAware_properties
Retreives the PropsAware property object. All properties on this object emit when set.
const PA = require('@develephant/props-aware')
let props = PA.props()
on(prop, cb)
Callback receives:
|Name|Purpose|
|----|-------|
|val
|The property value|
|prop
|The name of the property|
Listen for a property update.
const PA = require('@develephant/props-aware')
PA.on('score', (val, prop) => {
console.log(val)
})
has(prop) -> success_bool
Checks to see if a specific root property is housed in the PropsAware property table.
const PA = require('@develephant/props-aware')
let has_username = PA.has('username')
del(prop) -> success_bool
Removes a properties update listeners. This destroys all update listeners for the property, except the global onAll
.
let success = PA.del('score')
onAll(cb)
Callback receives:
|Name|Purpose|
|----|-------|
|val
|The property value|
|prop
|The name of the property|
Any part of the program can listen for all PA
property changes. When using this method, you need to filter the messages with control statements.
PA.onAll((val, prop) => {
console.log('changed', prop, val)
})
Property based flow control:
PA.onAll((val, prop) => {
if (prop === 'goback') {
console.log('go back')
} else if (prop === 'goforward') {
console.log('go forward')
}
})
//...somewhere else
/* PA Props reference */
let props = PA.props()
props.goback = true
//or
props.goforward = true
State based flow control (using on
):
PA.on('state', (val) => {
if (val === 'goback') {
console.log('go back')
} else if (val === 'goforward') {
console.log('go forward')
}
})
//...somewhere else
/* PA Props reference */
let props = PA.props()
props.state = 'goback'
//...or
props.state = 'goforward'
State based flow control is generally the better choice.
With the pattern above, its entirely possible to manipulate the bulk of your program with one
PA
property!
onDel(cb)
Callback receives:
|Name|Purpose|
|----|-------|
|prop
|The name of the property|
Fired when the del
method is used in the PropsAware object
PA.onDel((prop) => {
console.log('del', prop)
})
sync() --> success_bool
Dispatch all properties, to all listeners. Should not be used often, especially with large property objects.
Note: Any
onAll
listeners will be called with all emitted properties.
const PA = require('@develephant/props-aware')
//Set a property "silently"
PA.set('prop', 'val')
//Nevermind, resend everything
PA.sync()
Examples
In Classes
//ClassA.js
const PA = require('@develephant/props-aware')
class ClassA {
constructor() {
this.props = PA.props()
PA.on('score', (val) => {
console.log(val)
})
}
}
module.exports = ClassA
//ClassB.js
const PA = require('@develephant/props-aware')
class ClassB {
constructor() {
this.props = PA.props()
this.props.score = 100
}
}
module.exports = ClassB
Run
//main.js
const ClassA = require('./ClassA')
const ClassB = require('./ClassB')
const A = new ClassA()
const B = new ClassB() /* Class A will output "100" */
In Objects
//obj-one.js
/* Pull in PropsAware as PA */
const PA = require('@develephant/parse-aware')
const ObjA = {
props: PA.props(),
listen() {
PA.on('greeting', (val) => {
console.log(greeting)
})
}
}
module.exports = ObjA
//obj-two.js
/* Pull in PropsAware as PA */
const PA = require('@develephant/props-aware')
const ObjB = {
props: PA.props()
}
module.exports = ObjB
Run
//main.js
const objA = require('./obj-one')
const objB = require('./obj-two')
objA.listen()
objA.props.greeting = 'Hola' /* ObjA will output "Hola" */
//Props object is "global" so this works via objB:
objB.props.greeting = 'Hello' /* ObjA will output "Hello" */
A Function
const PA = require('@develephant/props-aware')
modules.export = function() {
this.props = PA.props()
this.props.greeting = 'Howdy'
PA.on('score', (val) => {
console.log(val)
})
}
// Assuming the Objects above...
/* ObjA will output 'Howdy' */
// Call score through Class B above
classB.props.score = 1000
/* This Function will output 1000 *\
/* ObjA will output 1000 */
Next Steps
- Immutability
^_^