agera
v1.0.0-alpha.4
Published
A small push-pull based signal library based on alien-signals algorithm.
Downloads
31
Maintainers
Readme
Agera
A small push-pull based signal library based on alien-signals algorithm.
Was created as reactivity system for nanoviews and Kida.
- Small. Around 1.8 kB for basic methods (minified and brotlied). Zero dependencies.
- Super fast as alien-signals.
- Designed for best Tree-Shaking: only the code you use is included in your bundle.
- TypeScript-first.
import { signal, computed, effect } from 'agera'
const $count = signal(1)
const $doubleCount = computed(() => count() * 2)
effect(() => {
console.log(`Count is: ${$count()}`);
}) // Console: Count is: 1
console.log($doubleCount()) // 2
$count(2) // Console: Count is: 2
console.log($doubleCount()) // 4Install
pnpm add agera
# or
npm i agera
# or
yarn add ageraAPI
Signal
Signal is a basic store type. It stores a single value.
import { signal } from 'agera'
const $count = signal(0)
$count($count() + 1)
// or
$count(count => count + 1)To watch signal changes, use the effect function. Effect will be called immediately and every time the signal changes.
import { signal, effect } from 'agera'
const $count = signal(0)
const stop = effect(() => {
console.log('Count:', $count())
return () => {
// Cleanup function. Will be called before effect update and before effect stop.
}
})
// later you can stop effect
stop()Computed
Computed is a signal that computes its value based on other signals.
import { computed } from 'agera'
const $firstName = signal('John')
const $lastName = signal('Doe')
const $fullName = computed(() => `${$firstName()} ${$lastName()}`)
console.log($fullName()) // John DoeeffectScope
effectScope creates a scope for effects. It allows to stop all effects in the scope at once.
import { signal, effectScope, effect } from 'agera'
const $a = signal(0)
const $b = signal(0)
const stop = effectScope(() => {
effect(() => {
console.log('A:', $a())
})
effectScope(() => {
effect(() => {
console.log('B:', $b())
})
})
})
stop() // stop all effectsAlso there is a possibility to create a lazy scope.
import { signal, effectScope, effect } from 'agera'
const $a = signal(0)
const $b = signal(0)
// All scopes will run immediately, but effects run is delayed
const start = effectScope(() => {
effect(() => {
console.log('A:', $a())
})
effectScope(() => {
effect(() => {
console.log('B:', $b())
})
})
}, true) // marks scope as lazy
// start all effects
const stop = start()
stop() // stop all effectsLifecycles
One of main feature of Agera is that you can create mountable signals. It allows to create lazy signals, which will use resources only if signal is really used in the UI.
- Signal is mounted when one or more effects is attached to it.
- Signal is unmounted when signal has no effects.
mountable method makes signal mountable.
onMounted lifecycle method adds callback for mount and unmount events.
import { signal, mountable, onMounted, effect } from 'agera'
const $count = mountable(signal(0))
onMounted($count, (mounted) => {
console.log('Signal is', mounted ? 'mounted' : 'unmounted')
})
// will mount signal
const stop = effect(() => {
console.log('Count:', $count())
})
// will unmount signal
stop()Batch updates
To batch updates you should wrap signal updates between startBatch and endBatch.
import { signal, startBatch, endBatch, effect } from 'agera'
const $a = signal(0)
const $b = signal(0)
effect(() => {
console.log('Sum:', $a() + $b())
})
// Effects will be called only once
startBatch()
$a(1)
$b(2)
endBatch()Skip tracking
To skip tracking of signal changes you should wrap signal into untracked function.
import { signal, untracked, effect } from 'agera'
const $a = signal(0)
const $b = signal(0)
effect(() => {
const a = $a()
untracked(() => {
const b = $b()
resumeTracking()
console.log('Sum:', a + b)
})
})
// or short variant
effect(() => {
console.log('Sum:', $a() + untracked($b))
})
// Will trigger effect run
$a(1)
// Will not trigger effect run
$b(2)Morph
morph methods allows to create signals that can change their getter and setter on the fly.
import { signal, morph, $$get, $$set, $$source } from 'agera'
const $string = signal('')
// Debounce signal updates
const $debouncedString = morph($string, {
[$$set]: debounce($string, 300)
})
// Lazy initialization
const $lazyString = morph($string, {
[$$get]() {
this[$$set]('Lazy string')
this[$$get] = this[$$source]
return 'Lazy string'
}
})isSignal
isSignal method checks if the value is a signal.
import { isSignal, signal } from 'agera'
isSignal(signal(1)) // trueWhy?
Key differences from alien-signals:
- Tree-shaking. Agera is designed to be tree-shakable. Only the code you use is included in your bundle. Alien-signals is not well tree-shakable.
- Size. Agera has a little bit smaller size for basic methods than alien-signals.
- Lifecycles. Agera has lifecycles for signals. You can listen to signal activation and deactivation events.
- Modificated
effectScope. Agera has a possibility to put effect scope inside another effect scope. Also there is a possibility to create a lazy scope. - Effects supports cleanup function.
