npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@develephant/props-aware

v1.2.1

Published

A "living" global properties data store. Dispatch with ease.

Downloads

4

Readme

PropsAware

Properties as dispatchers. A "living" global properties data store.

props-aware

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 an onAll 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

^_^


PropsAware ⋆ © 2017 develephant ⋆ Apache-2.0 license