fluids
This library is a tiny glue layer for observable events.
- Create a tree of observable values
- Let parent nodes send arbitrary events to children (for maximum flexibility)
- Stay small yet provide helpers for easier integration
Observe a value
Any object can be observed, but FluidValue
objects have strongly typed
events. Observed objects are basically event emitters whose listeners
receive every event, and they typically represent a single value.
To start observing:
import { addFluidObserver } from 'fluids'
// You can pass a function:
let observer = addFluidObserver(target, (event) => {
console.log(event)
})
// or pass an object:
observer = addFluidObserver(target, {
eventObserved(event) {
console.log(event)
},
})
To stop observing:
import { removeFluidObserver } from 'fluids'
removeFluidObserver(target, observer)
Create an observed object
You can extend the FluidValue
class for automatic TypeScript support with
fluids
-compatible libraries.
import { FluidValue, FluidObservable, callFluidObservers } from 'fluids'
// Your class can have multiple event types.
// The `type` and `parent` properties are required.
type RefEvent<T> = { type: 'change'; value: T; parent: Ref<T> }
// Use "interface merging" to attach the event types.
interface Ref<T> extends FluidObservable<RefEvent<T>> {}
// This example is an observable React ref.
class Ref<T> extends FluidValue<T> {
private _current: T
constructor(initialValue: T) {
// Passing a getter to super is only required
// if your class has no "get" method.
super(() => this._current)
this._current = initialValue
}
get current() {
return this._current
}
set current(value: T) {
this._current = value
// Send the change to all observers.
callFluidObservers(this, {
type: 'change',
value,
parent: this,
})
}
//
// These methods are completely optional.
//
protected observerAdded(count: number) {
if (count == 1) {
// Do something when the first observer is added.
}
}
protected observerRemoved(count: number) {
if (count == 0) {
// Do something when the last observer is removed.
}
}
}
If extending FluidValue
isn't an option, you can outfit an object or
prototype with the setFluidGetter
function:
import { setFluidGetter, callFluidObservers } from 'fluids'
// This example augments an existing React ref.
let { current } = ref
let get = () => current
setFluidGetter(ref, get)
Object.defineProperty(ref, 'current', {
get,
set(value) {
current = value
// Remember to notify any observers.
callFluidObservers(ref, {
type: 'change',
value,
parent: ref,
})
},
})
For libraries
The remaining functions are useful when making a fluids
-compatible library.
import {
hasFluidValue,
getFluidValue,
getFluidObservers,
callFluidObserver,
} from 'fluids'
// Check if a value is observable.
hasFluidValue(target)
// Get the current value. Returns `target` if not observable.
getFluidValue(target)
// Get the current observers (or null if none exist).
getFluidObservers(target)
// Call a single observer. Useful for special observation, like waterfalls.
callFluidObserver(observer, event)