informers
v0.1.1
Published
Enhanced reactive state management for React - ValueNotifier-like pattern
Downloads
3
Maintainers
Readme
informers
Enhanced reactive state management for React with a ValueNotifier-like pattern.
Installation
npm install informersFeatures
- ValueNotifier-like pattern inspired by Flutter's state management
- Force update mode to trigger re-renders even when value hasn't changed
- Silent updates to update state without triggering re-renders
- External stores with
createInformerfor state outside React components - React 18 concurrent mode support via
useSyncExternalStore - Zero dependencies (peer dependency on React 18+)
Quick Start
import { useInformer, createInformer, useInformerValue } from 'informers';
// Basic usage with useInformer hook
function Counter() {
const { value, update, updateCurrent } = useInformer(0);
return (
<div>
<p>Count: {value}</p>
<button onClick={() => update(value + 1)}>Increment</button>
<button onClick={() => updateCurrent(n => n + 1)}>Increment (functional)</button>
</div>
);
}API
useInformer<T>(initialValue: T, forceUpdate?: boolean): InformerState<T>
React hook for managing reactive state within a component.
Parameters
initialValue- The initial state valueforceUpdate- Iftrue, triggers re-render even when value is the same (default:false)
Returns InformerState<T>
| Property | Type | Description |
|----------|------|-------------|
| value | T | Current value |
| update | (value: T) => void | Update to a new value |
| updateCurrent | (updater: (current: T) => T) => void | Update based on current value |
| silentUpdate | (value: T) => void | Update without triggering re-render |
| rebuild | () => void | Force re-render with current value |
Examples
// Basic counter
function Counter() {
const { value, update } = useInformer(0);
return <button onClick={() => update(value + 1)}>{value}</button>;
}
// With forceUpdate for reference types
function ObjectState() {
const { value, update } = useInformer({ count: 0 }, true);
const increment = () => {
value.count++; // Mutate same object
update(value); // Still triggers re-render due to forceUpdate
};
return <button onClick={increment}>{value.count}</button>;
}
// Silent update for batching
function BatchedUpdates() {
const { value, silentUpdate, rebuild } = useInformer({ a: 0, b: 0 });
const batchUpdate = () => {
silentUpdate({ ...value, a: 1 }); // No re-render
silentUpdate({ ...value, a: 1, b: 2 }); // No re-render
rebuild(); // Single re-render with final state
};
return <button onClick={batchUpdate}>Batch</button>;
}createInformer<T>(initialValue: T): Informer<T>
Creates a standalone reactive store that can be used outside React components. Useful for global state, sharing state between components, or managing state in services.
Returns Informer<T>
| Property | Type | Description |
|----------|------|-------------|
| getValue | () => T | Get current value |
| setValue | (value: T) => void | Set value and notify subscribers |
| updateCurrent | (updater: (current: T) => T) => void | Update based on current value |
| subscribe | (listener: (value: T) => void) => () => void | Subscribe to changes |
Examples
// Create store outside React
const userStore = createInformer<User | null>(null);
// Update from anywhere (API calls, services, etc.)
async function login(credentials: Credentials) {
const user = await authApi.login(credentials);
userStore.setValue(user);
}
function logout() {
userStore.setValue(null);
}
// Use in components with useInformerValue
function UserProfile() {
const user = useInformerValue(userStore);
if (!user) return <LoginPrompt />;
return <div>Welcome, {user.name}</div>;
}useInformerValue<T>(informer: Informer<T>): T
React hook for subscribing to an external Informer store. Uses useSyncExternalStore for proper React 18 concurrent mode support.
const themeStore = createInformer<'light' | 'dark'>('light');
function ThemeToggle() {
const theme = useInformerValue(themeStore);
return (
<button onClick={() => themeStore.setValue(theme === 'light' ? 'dark' : 'light')}>
Current: {theme}
</button>
);
}Patterns
Global State Management
// stores/auth.ts
export const authStore = createInformer<AuthState>({
user: null,
isLoading: false,
});
export function setUser(user: User | null) {
authStore.updateCurrent(state => ({ ...state, user, isLoading: false }));
}
// components/Header.tsx
function Header() {
const { user } = useInformerValue(authStore);
return <div>{user?.name ?? 'Guest'}</div>;
}Form State
function Form() {
const { value: form, updateCurrent } = useInformer({
name: '',
email: '',
});
const setField = (field: string, value: string) => {
updateCurrent(f => ({ ...f, [field]: value }));
};
return (
<form>
<input
value={form.name}
onChange={e => setField('name', e.target.value)}
/>
<input
value={form.email}
onChange={e => setField('email', e.target.value)}
/>
</form>
);
}License
MIT
