Skip to content

eglove/store

Repository files navigation

"state management"

pnpm i @ethang/store

Create a store:

import { Store } from "@ethang/store";

const store = new Store({ count: 0 });

Update store

store.set((state) => {
    state.count += 1; // Immutable changes via Immer
})

Get from store

store.get(); // { count: 1 }

store.get(state => state.count); // 1

Subscribe to changes

const unsubscribe = store.subscribe((state) => {
    console.log(`Count is now ${state.count}`);
})

unsubscribe(); // Don't forget to clean up.

Subscribe HTML element

const counterButton = document.querySelector('#counter');

counterButton.onclick = () => store.set(state => {
    state.count += 1;
})

const bindFn = store.bind((state, element) => {
    element.textContent = state.count; 
})

bindFn(counterButton);

// Automatically cleans up after element is removed from DOM

Bind React Ref*

  • Fine-grained reactivity, does not trigger rerenders.
<button
    onClick={() => {
        store.set(state => {
            state.count += 1;
        })
    }}
    ref={store.bind((state, element) => {
        element.textContent = state.count;
    })}
/>

React useSyncExternalStore*

  • Sync w/ React reconciliation, triggers component rerenders on state changes.
import { useSyncExternalStore } from "react";

const state = useSyncExternalStore(
    listener => store.subcribe(listener),
    () => store.get(), // get client snapshot
    () => store.get(), // get server snaphot
);

<div>{state.count}</div>

React useSyncExternalStoreWithSelector*

  • A few less rerenders.
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector.js";

const count = useSyncExternalStoreWithSelector(
    listener => store.subscribe(listener),
    () => store.get(), // get client snapshot
    () => store.get(), // get server snaphot
    state => state.count,
);

<div>{count}</div>

Batch Updates

// Subscribers aren't notified until after work in set is done
store.set(state => {
    state.count += 1;
    state.count += 1;
    state.count += 1;
})

Async

Use TanStack Query