Skip to content

Instantly share code, notes, and snippets.

@mrclay
Last active August 8, 2022 03:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrclay/6ad71d1993e5d97e11aff1285c67eaf6 to your computer and use it in GitHub Desktop.
Save mrclay/6ad71d1993e5d97e11aff1285c67eaf6 to your computer and use it in GitHub Desktop.
Minimal teaful-like store using useSyncExternalStore()
import {useSyncExternalStore} from 'use-sync-external-store/shim';
const DOT = '.';
export default function createStore(defaultStore = {}) {
let allStore = defaultStore;
const listeners = new Set();
function subscribe(listener) {
listeners.add(listener);
return () => listeners.delete(listener);
}
function buildSetter(path = '') {
let fieldPath = Array.isArray(path) ? path : path.split(DOT);
return (newValue) => {
let prevStore = allStore;
let value = newValue;
if (typeof newValue === 'function') {
value = newValue(getField(path));
}
allStore = path ?
// Update a field
setField(allStore, fieldPath, value) :
// Update all the store
value;
};
}
function getField(path, fn = (a, c) => a?.[c]) {
if (!path) return allStore;
return (Array.isArray(path) ? path : path.split(DOT)).reduce(fn, allStore);
}
function setField(store, [prop, ...rest], value) {
let newObj = Array.isArray(store) ? [...store] : {...store};
newObj[prop] = rest.length ? setField(store[prop], rest, value) : value;
return newObj;
}
const usePath = (path = "") => {
const internalSetter = buildSetter(path);
const setter = newVal => {
internalSetter(newVal);
listeners.forEach((l) => l());
};
const current = useSyncExternalStore(
subscribe,
() => getField(path),
);
return [current, setter];
};
return {usePath};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment