@appboypov/informers
v1.0.0
Published
Reactive state management for React with Informer class and useInformerListenable hook - Flutter ValueNotifier pattern
Downloads
444
Maintainers
Readme
@appboypov/informers
Reactive state management for React with a ValueNotifier-like pattern inspired by Flutter.
Installation
npm install @appboypov/informersFeatures
- Informer class - Shared reactive state that can be used across components
- useInformerListenable hook - Subscribe to Informer changes with automatic re-renders
- InformerListenableBuilder components - Declarative component-based subscriptions
- React 18 concurrent mode support via
useSyncExternalStore - Zero dependencies (peer dependency on React 18+)
Quick Start
Shared State with Informer Class
import { Informer, useInformerListenable } from '@appboypov/informers';
// Create shared state (outside component)
const counter = new Informer(0);
function CounterDisplay() {
// Subscribe to changes
const count = useInformerListenable(counter);
return <p>Count: {count}</p>;
}
function CounterButtons() {
return (
<div>
<button onClick={() => counter.update(counter.value + 1)}>+</button>
<button onClick={() => counter.updateCurrent(n => n - 1)}>-</button>
</div>
);
}Builder Components
import { Informer, InformerListenableBuilder } from '@appboypov/informers';
const user = new Informer({ name: 'John', age: 30 });
function UserCard() {
return (
<InformerListenableBuilder
informer={user}
builder={(userData) => (
<div>
<h2>{userData.name}</h2>
<p>Age: {userData.age}</p>
</div>
)}
/>
);
}API
Informer<T>
A reactive state container that notifies listeners when its value changes.
const informer = new Informer(initialValue, options?);Constructor Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| forceUpdate | boolean | false | Notify listeners even when value is same reference |
Properties
| Property | Type | Description |
|----------|------|-------------|
| value | T | Current value (direct access, no subscription) |
Methods
| Method | Description |
|--------|-------------|
| update(value, options?) | Update value. Options: { doNotifyListeners?: boolean } |
| updateCurrent(updater, options?) | Update using function: (current) => newValue |
| rebuild() | Force notify listeners without changing value |
| dispose() | Clear all listeners |
Example
const counter = new Informer(0);
// Direct access (no subscription)
console.log(counter.value); // 0
// Update and notify
counter.update(5);
// Update with function
counter.updateCurrent(n => n + 1);
// Silent update (no re-render)
counter.update(10, { doNotifyListeners: false });
// Force rebuild
counter.rebuild();
// Cleanup
counter.dispose();useInformerListenable<T>(informer: Informer<T>): T
React hook that subscribes to an Informer and returns its current value. Component re-renders when the Informer's value changes.
const counter = new Informer(0);
function Counter() {
const count = useInformerListenable(counter);
return <p>Count: {count}</p>;
}InformerListenableBuilder<T>
Component that rebuilds when an Informer's value changes.
<InformerListenableBuilder
informer={counter}
builder={(value) => <p>Count: {value}</p>}
/>InformerListenableBuilder2<T1, T2>
Component that rebuilds when either of two Informers change.
const firstName = new Informer('John');
const lastName = new Informer('Doe');
<InformerListenableBuilder2
informer1={firstName}
informer2={lastName}
builder={(first, last) => <p>{first} {last}</p>}
/>InformerListenableBuilder3<T1, T2, T3>
Component that rebuilds when any of three Informers change.
<InformerListenableBuilder3
informer1={firstName}
informer2={lastName}
informer3={age}
builder={(first, last, userAge) => (
<p>{first} {last}, {userAge} years old</p>
)}
/>Patterns
Global State
// stores/counter.ts
export const counterStore = new Informer(0);
// components/Counter.tsx
import { counterStore } from '../stores/counter';
function Counter() {
const count = useInformerListenable(counterStore);
return <p>{count}</p>;
}Service with Reactive State
class UserService {
readonly currentUser = new Informer<User | null>(null);
readonly isLoading = new Informer(false);
async login(email: string, password: string) {
this.isLoading.update(true);
try {
const user = await api.login(email, password);
this.currentUser.update(user);
} finally {
this.isLoading.update(false);
}
}
}
// In component
function UserStatus() {
const user = useInformerListenable(userService.currentUser);
const loading = useInformerListenable(userService.isLoading);
if (loading) return <p>Loading...</p>;
return <p>Welcome, {user?.name}</p>;
}License
MIT
