JSPM

  • Created
  • Published
  • Downloads 11
  • Score
    100M100P100Q68923F
  • License MIT

The simplest watcher for your state

Package Exports

  • watch-state

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (watch-state) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

watch-state

NPM minzipped size downloads license
The simplest watcher of your state.

Installation

npm

npm i watch-state

yarn

yarn add watch-state

Using

Simple example:

You can create an instance of State and watch its value.

import {Watch, State} from 'watch-state'

const count = new State(0)

new Watch(() => console.log(count.value))
// console.log(0)

count.value++
// console.log(1)

count.value++
// console.log(2)
Update argument:

You can check if the watching ran first by update argument.

const count = new State(0)

new Watch(update => {
  console.log(update, count.value)
})
// console.log(false, 0)

count.value++
// console.log(true, 1)

count.value++
// console.log(true, 2)
Deep watch:

You can use Watch inside watcher. Each watcher reacts on that states which used only inside it.

const watching = new State(true)
const state = new State(0)
let test = 0

new Watch(() => {
  test++
  if (watching.value) {
    new Watch(() => console.log(state.value))
  }
})
// console.log(0), test = 1

state.value++
// console.log(1), test = 1

watching.value = false
// test = 2

state.value++
// nothing happens
Cache:

You may cache computed values. The watcher will not be triggered while new result is the same.

const name = new State('Mike')
const surname = new State('Deight')

const fullName = new Cache(() => (
  `${name.value} ${surname.value[0]}`
))

new Watch(() => console.log(fullName.value))
// console.log('Mike D')

surname.value = 'D8'
// nothing happens

surname.value = 'Mighty'
// console.log('Mike M')

The computing will be triggered only when a state inside the cache will be changed. So you can modify data only when it's needed.

const list = new State(['foo', 'bar', 'baz'])

const sortedList = new Cache(() => {
  console.log('computing')
  return list.value.sort()
})
// nothing happens

const value = sortedList.value
// console.log('computing')

console.log(sortedList.value)
// console.log(['bar', 'baz', 'foo'])

console.log(value === sortedList.value)
// console.log(true)

list.value = ['b', 'c', 'a']
// nothing happens

console.log(sortedList.value)
// console.log('computing')
// console.log(['a', 'b', 'c'])
Mixer:

Mixer works like Cache but you can mix some states and usual variables.

let count = 0

const text = new Mixer(() => {
  return count++ ? (
    `Updated: ${count - 1}`
  ) : null
})

const watcher = new Watch(() => {
  console.log(
    text.value ? text.value : 'First render'
  )
})
// console.log('First render')

watcher.update()
// console.log('Updated: 1')

watcher.update()
// console.log('Updated: 2')

you cannot use mixer inside cache, it'll be fixed in the future

Event:

Use createEvent when you change several states to run their watchers after the event finished.

const name = new State('Mike')
const surname = new State('Deight')

const setFullName = createEvent(fullName => {

  const [newName, newSurname] = fullName.split(' ')

  name.value = newName
  surname.value = newSurname

})

new Watch(() => {
  console.log(name.value, surname.value)
})
// console.log('Mike', 'Deight')

setFullName('Michael Mighty')
// console.log('Michael', 'Mighty')
Decorators:

You can use decorators with watch-sate.
Available: watch state cache mixer event

import {watch, state, cache, event, mixer} from 'watch-state'

class Counter {
  // fields
  @state value = 1

  // accessors
  @mixer get sqrt () {
    return Math.sqrt(this.value)
  }
  @cache get square () {
    return this.value ** 2
  }

  // methods
  @event tick () {
    this.value++
  }
  @watch run () {
    console.log(this.value, this.square)
  }
}


const counter = new Counter()

counter.run()
// console.log(1, 1)

counter.tick()
// console.log(2, 4)
Typescript:

Generic of State

const key = new State<string | number>()

key.value = false
// error, you can use only streng or number

Generic of Cache or Mixer

new Cache<string>(() => false)
// error, target of cache should return string

new Mixer<string>(() => false)
// error, target of mixer should return string

Other

Watch.destructor()

You can stop watching by destructor method of Watch.

const count = new State(0)

const watcher = new Watch(() => console.log(count.value))
// console.log(0)

count.value++
// console.log(1)

watcher.destructor()

count.value++
// nothing happens
Watch.update()

Forced update

let count = 0
const watcher = new Watch(() => console.log(++count))
// console.log(1)

watcher.update()
// console.log(2)
Watch.onDestructor()

You can react on destruction of Watch by onDestructor method.

const watcher = new Watch(() => {})

watcher.onDestructor(() => console.log('destructor'))

watcher.destructor()
// console.log('destructor')

onDestructor returns this so you can use fluent interface.

const watcher = new Watch(() => {})
  .onDestructor(() => console.log('destructor'))

watcher.destructor()
// console.log('destructor')

Or you can use onDestructor function inside a watcher.

import {Watch, onDestructor} from 'watch-state'

const watcher = new Watch(() => {
  // do something
  onDestructor(() => console.log('destructor'))
})

watcher.destructor()
// console.log('destructor')

Unstable functionality

getDecor

You can get State, Cache or Mixer of decorated field by getDecor

class Test {
  @state field1 = 1
}

const test = new Test()
const stateOfField1 = getDecor(test, 'field1')

console.log(stateOfField1 instanceof State)
// console.log(true)

console.log(stateOfField1.value)
// console.log(1)

Generic of getDecor. Provide 'state', 'cache' or 'mixer' as the first generic type and typeof the first argument as the second one.

class Test {
  @state field1 = 1
}

const test = new Test()
const stateOfField1 = getDecor<'state', typeof test>(test, 'field1')

stateOfField1.value = '2'
// error, stateOfField1 State has number type
getDecors

You can get a list with State, Cache or Mixer of target fields by getDecors

class Test {
  @state field1 = 1
}

const test = new Test()
const decors = getDecors(test)

console.log(Object.keys(decors))
// console.log(['field1'])

console.log(decors.field1.value)
// console.log(1)

Generic of getDecors.

Provide list of props equals 'state', 'cache' or 'mixer' as the first generic type and typeof the first argument as the second one.

class Test {
  @state field1 = 1
}

const test = new Test()
const decors = getDecors<{field1: 'state'}, typeof test>(test)

decors.field1.value = '2'
// error, field1 has type of number

If the first argument type of getDecor or getDecors equals this then provide this as the second generic prop

getDecors<{field1: 'state'}, this>(this)

Issues

If you find a bug or have a suggestion, please file an issue on GitHub
issues


stars watchers