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

immutable-state-controller

v1.0.0

Published

Immutable state change controller

Downloads

8

Readme

Immutable State Controller

npm version Node CI

A small library to work with immutable state without a lot of boilerplate, but with a lot of type-safety using TypeScript.

immutable-state-controller lets you view and mutate state using a Controller, while also providing access to immutable copies of state using Snapshots.

Each Controller or Snapshot provides a method to change the state. All changes are immediately visible in the Controller, but a Snapshot never changes.

Installation

npm install immutable-state-controller

Example

import { controllerWithInitialValue } from 'immutable-state-controller'

const controller = controllerWithInitialValue({
	a: 'Hello world',
	b: 42,
	c: {
		d: 'Nested okay',
		e: ['A', 'B', 'C', 'D'],
	},
})

controller.get('a').setValue('Bye bye')

const immutableValue = controller.snapshot('c')

Snapshots

A Snapshot is an immutable snapshot of the state from a controller.

You can obtain snapshots from the Controller of either the whole state, or of parts of the state. Any changes you make via the Controller will be reflected immediately in the controller's value, but the Snapshot never changes as it is immutable — ensuring that you retain a consistent, immutable view of the state.

const aSnapshot = controller.snapshot('a') // Snapshot<string>
// aSnapshot.value === 'Hello world'

aSnapshot.change('Bye bye')
// controller.value.a === 'Bye bye'
// aSnapshot.value === 'Hello world'

Snapshots can be created for any type, including objects:

const cSnapshot = controller.snapshot('c') // Snapshot<{ d: string, e: string[] }>
// cSnapshot.value.d === 'Nested okay'

cSnapshot.change({
	d: 'Changed',
	e: ['E'],
})

// controller.value.c.d === 'Changed'
// cSnapshot.value.d === 'Nested okay'

You can also create controllers for nested objects in order to access further nested snapshots:

const eSnapshot = controller.get('c').snapshot('e') // Snapshot<string[]>

// eSnapshot.value == ['E']

eSnapshot.change(['F', 'G'])
// controller.value.c.e == ['F', 'G']

This pattern is powerful when sharing state between multiple pieces of code while wanting to ensure an immutable and consistent view of that state; creating and sharing a new snapshot of the state when appropriate.

Nested controllers

You can obtain a controller for a nested value. Any changes to the nested controller are also reflected in the parent controller.

const cController = controller.get('c')
cController.setValue({
	d: 'Gone',
	e: [],
})

Array Controllers also support map and find to access nested controllers:

const eController = controller.get('e')
eController.map((controller: Controller<string>, index: number, array: string[]) => controller.value.toLowerCase()) == ['a', 'b', 'c', 'd']

const ecController = eController.find((value: string, index: number, array: string[]) => value === 'C')
ecController.setValue('c')

Controller reference

A Controller manages a value. It is a generic type, where its type represents the type of value it contains.

Some examples of controllers:

  • Controller<string> for a controller that simply contains a string value
  • Controller<Person> for a controller that contains an object
  • Controller<Person[]> for a controller that contains an array

Accessing the value

You can get the value from the controller using the value property, and set it using the setValue method.

|Property / Method|Description| |--------|-----------| |value|The value in the controller.| |setValue(newValue: T)|Set the value in the controller.|

Note that the value in the controller is live, ie. it is independent of React’s render cycle.

Nested values

When the controller contains an array or an object, you can create sub-controllers to access specific parts of the controller. Changes in sub-controllers are immediately reflected in the parent controller.

Array controllers

When a controller contains an array value, these methods are applicable:

|Method|Description| |------|-----------| |get(index: number)|Returns a sub-controller for the value at the given index.| |set(index: number, newValue)|Set the value at the given index.| |map(callback)|Map over the values. The callback receives a controller for each value as its first argument and an index as its second.| |find(predicate)|Returns the first value in the controller that matches the predicate. The predicate signature is (value: T, index: number, array: T[]) => boolean. The find method returns a Controller for the found value, or undefined if not found.| |findIndex(predicate)|Returns the index of the first value in the controller that matches the predicate. The predicate signature is (value: T, index: number, array: T[]) => boolean.|

Object controllers

When a controller contains an object value, these methods are applicable:

|Method|Description| |------|-----------| |get(prop: string)|Return a sub-controller for the value of the given property.| |set(prop: string, newValue)|Set the value of the given property.| |get(prop: string, index: number)|Returns a sub-controller for the value at the given index of the array in the given property.| |map(prop: string, callback)|Map over the values in the given array-valued property. The callback receives a controller for each value as its first argument and an index as its second.| |find(prop: string, predicate)|Returns the first value in the given array-valued property that matches the predicate. The predicate signature is (value: T, index: number, array: T[]) => boolean. The find method returns a Controller for the found value, or undefined if not found.| |findIndex(prop: string, predicate)|Returns the index of the first value in the given array-values property that matches the predicate. The predicate signature is (value: T, index: number, array: T[]) => boolean.|

Listening for changes

You can add change listeners to a controller. The change listener will be called when the value in the controller is changed.

controller.addChangeListener(function(newValue: T) {

})

Listeners can also be removed:

controller.removeChangeListener(listenerFunc)

Listeners can be added with a "tag" and then removed all at once:

controller.addChangeListener(listenerFunc, 'myTag')
controller.removeAllChangeListeners('myTag')

Or all change listeners can be removed:

controller.removeAllChangeListeners()