4.0.0 • Published 3 years ago

mobx-collection-watch v4.0.0

Weekly downloads
481
License
MIT
Repository
github
Last release
3 years ago

Mobx Collection Watch

Codecov CI semantic release

Install

npm install mobx-collection-watch

Table of Contents

How it works

Monitor mobx arrays and maps for changes. Works with mobx enforceActions mode.

This package is using mobx observe to listen to changes on arrays and maps.

Adding items

Monitor collection for new items. Every time something is added to the collection a callback will run with the new value that has been added. Replacing items at a particular array index also works.

array:

import { observable } from 'mobx'
import { arrayAdded } from 'mobx-collection-watch'

const collection = observable([])

const dispose = arrayAdded(collection, (items, disposer) => {
  // items is array of newl
  items.forEach((item) => {
    console.log(`added ${item}`) // 1,2,3
  })

  // stop responding to new items from inside the callback
  disposer()
})

collection.push(1, 2, 3)

// this will also work
collection[3] = 4

//stop responding for new items
dispose()

map:

import { observable } from 'mobx'
import { mapAdded } from 'mobx-collection-watch'

const collection = observable(new Map())

//callback
const cb = function (items, dispose) {
  items.forEach((item) => {
    console.log(item) // {key:'a',value:'one'} , {key:'b',value:'two'}
  })

  //run dispose if you want to stop tracking
  dispose()
}

const dispose = mapAdded(collection, cb)

collection.set('a', 'one')
collection.set('b', 'two')

Removing items

Every time something is removed from the collection a callback will run with the value that has been removed.

array: Replacing items at a particular array index will not trigger the callback.

import { observable } from 'mobx'
import { arrayRemoved } from 'mobx-collection-watch'

const collection = observable([1, 2, 3, 4])

const dispose = arrayRemoved(collection, (items, disposer) => {
  // items is array of newl
  items.forEach((item) => {
    console.log(`removed ${item}`)
  })

  // stop responding to removed items from inside the callback
  disposer()
})

collection.pop() // trigger callback with [4]
collection.splice(0, 2) //trigger callback with [1,2]

// NOTE: this will not work!
collection[0] = 5
//stop responding for new items
dispose()

map: Maps interface works nearly the same as the array interface. The only difference is that when there is a change the payload will be the map key and the value as opposed to only the value in case of arrays.

import { observable } from 'mobx'
import { mapRemoved } from 'mobx-collection-watch'

const collection = observable(
  new Map([
    ['a', 'one'],
    ['b', 'two']
  ])
)

//callback
const cb = function (items, dispose) {
  items.forEach((item) => {
    console.log(item) // {key:'a',value:'one'} , {key:'b',value:'two'}
  })

  //run dispose if you want to stop tracking
  dispose()
}

const dispose = mapRemoved(collection, cb)

collection.clear()

Replacing items

You can track which items in the collection have been replaced.

array: Please note that this is one to one tracking of new and replaced items, so it only works when items in the array are replaced directly by index collection[0]=newItem

This will not trigger the callback: collection.splice(0,1,newItem) The callback will receive an array with objects that represent the data for every replaced element:

// index at 0 which holds the value 1 was replaced with the value 3
;[
  {
    index: 0,
    oldValue: 1,
    newValue: 3
  }
]
import { observable } from 'mobx'
import { arrayReplaced } from 'mobx-collection-watch'

const collection = observable([1, 2, 3, 4])
const cb = function () {}

const dispose = arrayReplaced(collection, (replacedData, disposer) => {
  replacedData.forEach((data) => {
    console.log(data) // e.g. {index:0, oldValue:1,newValue:3}
  })
})

collection[0] = 3 // {index:0, oldValue:1,newValue:3}
collection[1] = 4 // {index:1 oldValue:2,newValue:4}

//stop responding to new replacements
dispose()

map: Map replacement payload callback data:

// map key 'foo' which holds the value 1 is replaced with value 2
;[
  {
    key: 'foo,
    oldValue: 1,
    newValue: 2
  }
]
import { observable, runInAction } from 'mobx'
import { mapReplaced } from 'mobx-collection-watch'

const collection = observable(
  new Map([
    ['a', 'one'],
    ['b', 'two']
  ])
)

//callback
const cb = function (items, dispose) {
  items.forEach((item) => {
    console.log(item) // {key:'a',oldValue:'one',newValue:'foo'} , {key:'b',oldValue:'two',newValue:'bar'}
  })

  //run dispose if you want to stop tracking
  dispose()
}

const dispose = mapUpdated(collection, cb)

runInAction(() => {
  collection.set('a', 'foo')
  collection.set('b', 'bar')
})

Tracking changes to the items in the collection

This functionality enables you to track changes to the items in the collection not the collection itself. Every time the item is added to the collection you can start tracking changes to that item. So you are not tracking what get's added or removed from the collection, rather you are tracking individual item changes that are in the collection. And when the item is removed from the collection, tracking for that item will stop. Under the hood it uses mobx reaction()

array:

import { observable, toJS } from 'mobx'
import { arrayItemChanged } from 'mobx-collection-watch'

const item1 = { id: 1, name: 'Ann' }
const item2 = { id: 2, name: 'Jenna' }

const collection = observable([item1, item2])

//function that determines what properties are tracked.
//everything accessed inside the function will be tracked.
const selectorFn = (item) => {
  return toJS(item) // monitor all properties
  //or
  // monitor only the name
  return items.name // this will be the "data" prop in cb()
}

// callback will be triggered when any properties accessed inside the selectorFn function change
const cb = (data, item, reaction) => {
  // data is what is returned from the selectorFn
  console.log(`data ${data}`) // item.name
  console.log(`item changed ${item}`) // item that has changed
  //if you want to stop the reaction for that particular item
  reaction.dispose()
}

const options = {} // options is directly passed to the mobx reaction() options

const dispose = arrayItemChanged(collection, selectorFn, cb, options)

// stop tracking all items.
dispose()

map:

import { observable, runInAction } from 'mobx'
import { mapItemChanged } from 'mobx-collection-watch'

// add observable items
const item1 = observable({
  value: {
    id: 1,
    name: 'one'
  }
})

//callback
const cb = function (data, item, reaction) {
  console.log(data) //5
  console.log(item) // item2

  //run dispose if you want to stop tracking
  reaction.dispose()
}

const collection = observable(new Map())
collection.set('a', item1)

//change item1
item1.value.id = 5

Working with mobx actions

When working with mobx actions, using for example runInAction. All modifications to the collection are batched. In the next example, callback function will be triggered only once.

// ...code

runInAction(() => {
  collection.push(1)
  collection.push(2)
  collection.push(3, 4)
})

//callback will be triggered once with [1,2,3,4]

Delayed callbacks

The callback can be called with a delay, when that happens all changes to the collection are batched (similar to how runInAction works). Delay is supported by all collection functions.

The next example is tracking additions to the array with a delay of 100ms, and it will be triggered only once. Notice that we are not running inside mobx actions

import { observable } from 'mobx'
import { arrayAdded } from 'mobx-collection-watch'

const collection = observable([])
const cb = (items, disposer) => {
  items.forEach((item) => {
    console.log(`added ${item}`)
  })
}

//callback will be triggered only once with [1,2,3,4,5]
const dispose = arrayAdded(collection, cb, 100)

collection.push(1)
collection.push(2)
collection.push(3, 4, 5)
4.0.0

3 years ago

3.0.0

3 years ago

2.4.0

4 years ago

2.3.2

4 years ago

2.3.1

4 years ago

2.3.0

4 years ago

2.2.0

4 years ago

2.0.13

4 years ago

2.0.14

4 years ago

2.0.11

4 years ago

2.0.12

4 years ago

2.0.9

4 years ago

2.0.10

4 years ago

2.0.8

4 years ago

2.1.0

4 years ago

2.0.7

4 years ago

2.0.6

4 years ago

2.0.5

4 years ago

2.0.4

4 years ago

2.0.3

4 years ago

2.0.2

4 years ago

2.0.1

4 years ago

2.0.0

4 years ago

1.0.0

4 years ago