DEV Community

Adam Golan
Adam Golan

Posted on

1

Reactive State Management Without Libraries

Simple, Powerful State Handling

export class State<IState = Record<string, unknown>> {
    private data: Map<keyof IState, IState[keyof IState]> = new Map();
    private subscribers: Map<string, ((...args: any[]) => void)[]> = new Map();

    set(name: keyof IState, value: IState[keyof IState]): void {
        this.data.set(name, value);
        this.publish(`change:${String(name)}`, value);
    }

    get(name: keyof IState): any | undefined {
        return this.data.get(name);
    }

    has(name: keyof IState): boolean {
        return this.data.has(name);
    }

    clear(): void {
        this.data.clear();
        this.publish('clear');
    }

    publish(name: string, ...args: any[]): void {
        this.subscribers.get(name)?.forEach(fn => fn(...args));
    }

    subscribe(name: string, fn: (...args: any[]) => void): void {
        this.subscribers.has(name)
            ? this.subscribers.get(name)!.push(fn)
            : this.subscribers.set(name, [fn]);
    }

    unsubscribe(name: string, fn: (...args: any[]) => void): void {
        if (this.subscribers.has(name)) {
            const idx = this.subscribers.get(name)!.indexOf(fn);
            if (idx > -1) this.subscribers.get(name)!.splice(idx, 1);
        }
    }

    unsubscribeAll(name: string): void {
        this.subscribers.delete(name);
    }
}
Enter fullscreen mode Exit fullscreen mode

Data Binding: The Real Reactivity

React Integration Example

function UserProfile() {
    const state = new State<{
        name: string;
        age: number;
    }>();

    const [userData, setUserData] = useState({
        name: state.get('name') || '',
        age: state.get('age') || 0
    });

    // Automatic reactivity through state changes
    state.subscribe('change:name', (newName) => {
        setUserData(prev => ({ ...prev, name: newName }));
    });

    return (
        <div>
            <input 
                value={userData.name}
                onChange={({target}) => state.set('name', target.value)} 
            />
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

Why This Beats Reactive Libraries

  • Zero external dependencies
  • Minimal bundle size
  • Native JavaScript performance
  • Simple, intuitive API
  • Built-in change tracking

Key Reactive Principles

  1. State changes trigger updates
  2. Subscribers automatically notified
  3. No complex stream processing
  4. Direct, predictable data flow

Conclusion

Reactivity isn't about libraries. It's about understanding how data flows and changes. This implementation proves that powerful state management is native to JavaScript.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more