Skip to content

Commit

Permalink
fix: createStore
Browse files Browse the repository at this point in the history
  • Loading branch information
djalmajr committed Apr 27, 2023
1 parent 995063e commit b8c1c89
Showing 1 changed file with 42 additions and 35 deletions.
77 changes: 42 additions & 35 deletions src/createStore.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,70 @@
import { clone } from './clone';
import { createEmitter } from './createEmitter';
import { isFunction } from './isFunction';
import { isObject } from './isObject';
import { omit } from './omit';
import { Obj } from './types';

type Store<T> = T & { subscribe(fn: (s: T) => void): () => never };

const { emit, on } = createEmitter();
const { defineProperty, getOwnPropertyDescriptor } = Object;

const handler = () => ({
get(obj: Obj, key: string) {
if (key === '_isProxy') return true;
const d = Object.getOwnPropertyDescriptor(obj, key);
const nok = isObject(obj[key]) && !obj[key]._isProxy && d?.writable;
nok && (obj[key] = new Proxy(obj[key], handler()));
return obj[key];
},
set(obj: Obj, key: string, val: unknown) {
if (obj[key] !== val) (obj[key] = val), emit('update');
return true;
},
deleteProperty(obj: Obj, key: string) {
delete obj[key];
emit('update');
return true;
},
});

export function createStore<T = Obj>(init: T): T & { subscribe(fn: (s: T) => void): () => never } {
const fns = <Function[]>[];
const subscribe = (fn: Function) => {
if (!fns.includes(fn)) fns.push(fn);
return () => {
const idx = fns.findIndex((l) => l === fn);
idx !== -1 && fns.splice(idx, 1);
};
function handler() {
return {
get(obj: Obj, key: string) {
if (key === '_isProxy') return true;
const d = getOwnPropertyDescriptor(obj, key);
const nok = isObject(obj[key]) && !obj[key]._isProxy && d?.writable;
nok && (obj[key] = new Proxy(obj[key], handler()));
return obj[key];
},
set(obj: Obj, key: string, val: unknown) {
if (obj[key] !== val) (obj[key] = val), emit('update');
return true;
},
deleteProperty(obj: Obj, key: string) {
delete obj[key];
emit('update');
return true;
},
};
}

export function createStore<T extends object>(data: T): Store<T> {
const fns = <Function[]>[];
const init = clone(data);

defineProperty(init, 'subscribe', {
value(fn: Function) {
if (!fns.includes(fn)) fns.push(fn);
return () => {
const idx = fns.findIndex((l) => l === fn);
idx !== -1 && fns.splice(idx, 1);
};
},
});

let nextTickId = 0;
const store = new Proxy(Object.assign(init as never, { subscribe }), handler());
const store = new Proxy(init, handler());
on('update', () => {
const data = omit('subscribe', store);
if (nextTickId) cancelAnimationFrame(nextTickId);
nextTickId = requestAnimationFrame(() => fns.forEach((fn) => fn(data)));
});

for (const prop in init) {
const d = Object.getOwnPropertyDescriptor(init, prop);
const d = getOwnPropertyDescriptor(init, prop);
if (d?.get && d?.configurable) {
Object.defineProperty(init, prop, {
defineProperty(init, prop, {
get: d.get.bind(store),
configurable: false,
});
} else if (isFunction(init[prop])) {
Object.defineProperty(init, prop, {
defineProperty(init, prop, {
value: (init[prop] as any).bind(store),
configurable: false,
writable: false,
});
}
}

return store as never;
return store as Store<T>;
}

0 comments on commit b8c1c89

Please sign in to comment.