DEV Community

Adam Golan
Adam Golan

Posted on

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.

Top comments (0)