chrono-state-z
v2.1.0
Published
Intent-first, logic-centric reactive state runtime with atoms, computed, async state, effects, and deterministic scheduling. React-agnostic core.
Maintainers
Readme
⏱️ chrono-state-z
LIVE EXAMPLE
chrono-state-z is a reactive, intent-first state runtime designed to keep business logic outside React.
It provides atoms, computed values, async state, effects, scheduling, with a headless core and thin React bindings.
React renders state. Logic lives elsewhere.
✨ Why chrono-state-z?
Use chrono-state-z when you need:
- Predictable state & side-effects
- Complex async flows (fetch → invalidate → retry)
- Logic reusable outside React (tests, workers, backend)
- Fine-grained reactivity (no global rerenders)
- Clear separation between logic and view
🧠 Mental Model
- Atom — small reactive state unit
- Computed — derived value, cached and reactive
- AsyncAtom — async state with suspense-style read
- Effect — reactive side-effect runner
- Scheduler — priority-based execution control
- Store / Intent — intent-driven state orchestration
- React hooks — thin bindings over the headless core
📦 Installation
# npm install react ## for react
npm install intentx-core-z chrono-state-z🔹 Core Usage
Atom & Computed
import { atom, computed } from 'chrono-state-z'
const count = atom(0)
const double = computed(() => count() * 2)
count.set(5)
console.log(count()) // 5
console.log(double()) // 10Reactive Effects
import { atom, effect } from 'chrono-state-z'
const count = atom(0)
const dispose = effect(() => {
console.log('Count changed:', count())
})
count.set(1) // logs: Count changed: 1
dispose()🔹 Async State
AsyncAtom
import { asyncAtom } from 'chrono-state-z'
const user = asyncAtom(async () => {
const res = await fetch('/api/user')
return res.json()
})
await user.load()
const u = user()Invalidate & refetch:
user.invalidate()
user.invalidate('low')AsyncComputed
import { asyncComputed, atom } from 'chrono-state-z'
const count = atom(2)
const doubleAsync = asyncComputed(async () => {
await new Promise(r => setTimeout(r, 50))
return count() * 2
})
await doubleAsync()🔹 Transactions (Batch updates)
import { atom, transaction } from 'chrono-state-z'
const a = atom(1)
const b = atom(2)
transaction(() => {
a.set(10)
b.set(20)
})
console.log(a(), b())🔹 Store & Intent (Logic Layer)
import { createStore } from 'chrono-state-z'
type State = {
saving: boolean
value: string
}
const logic = createStore<State>({
saving: false,
value: ''
})
logic.on('SAVE', async ({ state, setState }) => {
setState(s => { s.saving = true })
await fakeApiSave(state().value)
setState(s => { s.saving = false })
})Emit intent:
await logic.emit('SAVE')🔹 React Integration
useAtom
import { atom, useAtom } from 'chrono-state-z'
const count = atom(0)
function Counter() {
const value = useAtom(count)
return (
<button onClick={() => count.set(value + 1)}>
Count: {value}
</button>
)
}useComputed
import { computed, useComputed } from 'chrono-state-z'
const total = computed(() => price() * qty())
function TotalView() {
const value = useComputed(total)
return <div>Total: {value}</div>
}useAtomSelector
// atom() returns a callable reactive value
const user = atom({ id: 1, name: 'Alice', age: 20 })
function Username() {
const name = useAtomSelector(user, u => u.name)
return <span>{name}</span>
}useStore
import { useStore } from 'chrono-state-z'
import type { Store } from 'chrono-state-z'
function StoreView({ store }: { store: Store<any> }) {
const state = useStore(store)
return <pre>{JSON.stringify(state, null, 2)}</pre>
}
useStoreSelector
// Only re-renders when saving changes, not the whole store.
import { useStoreSelector } from 'chrono-state-z'
function SavingBadge({ store }) {
const saving = useStoreSelector(store, s => s.saving)
// const saving = useStoreSelector(
// store,
// s => s.meta,
// shallowEqual
// )
return saving ? 'Saving...' : 'Idle'
}
selectorshould be pure and stable.- If you need dynamic selection, memoize the selector.
useWatch
import { useWatch } from 'chrono-state-z'
function AuthGuard() {
useWatch(
() => user(),
(u) => {
if (u?.role === 'admin') {
redirect('/admin')
}
}
)
return null
}
AsyncAtom
const user = asyncAtom(fetchUser)
// load explicitly
await user.load()
// read value (throws / suspends if not ready)
const u = user()🧩 Architecture Pattern
import { asyncAtom, computed } from 'chrono-state-z'
export function createUserLogic() {
const user = asyncAtom(fetchUser)
const name = computed(() => user()?.name ?? 'Guest')
return {
user,
name,
reload: () => user.invalidate()
}
}function UserView({ logic }) {
const user = useAtom(logic.user)
const name = useComputed(() => logic.name())
return (
<>
<div>Hello {name}</div>
<button onClick={logic.reload}>Reload</button>
</>
)
}
📊 Comparison with Other Libraries
| Feature | chrono-state-z | Redux | Zustand | Jotai | |---------------------------|---------------- |-------|--------- |------- | | Fine-grained reactivity | ✅ | ❌ | ⚠️ | ✅ | | Async primitives | ✅ | ⚠️ | ❌ | ⚠️ | | Intent / effect layer | ✅ | ⚠️ | ❌ | ❌ | | Scheduler / priority | ✅ | ❌ | ❌ | ❌ | | Headless (non-React) core | ✅ | ❌ | ⚠️ | ❌ | | Testability | ✅ | ⚠️ | ⚠️ | ❌ |
🚫 Anti-patterns
- Putting business logic inside React components
- Mutating atom values directly
- Coupling domain logic to UI events
- Skipping computed / effects for orchestration
🧠 Philosophy
- Logic lives outside React
- Deterministic, testable state
- Effects are explicit and traceable
- UI is a pure projection of state
📜 License
MIT
