diff --git a/examples/example.ts b/examples/example.ts index d1cc292..c0181d1 100644 --- a/examples/example.ts +++ b/examples/example.ts @@ -37,25 +37,16 @@ const { beep } = useMyCustomStore( const useMyAtom = atom( beep, - (set) => async (next: string) => set(next) + (set) => async (next: string) => set(next), ); -const { - value, - update -} = useMyAtom((state) => ({ - value: state, - update: state.setState -})); - -console.log(update(value + 'Test')); - - -const test = (set) => async (next: string) => set(next) +const [value, update] = useMyAtom((state) => state); +console.log(update(value + "Test")); +const test = (set) => async (next: string) => set(next); const [myValue, setState] = useAtom( - beep, - (set) => async (next: string) => set(next) -) \ No newline at end of file + beep, + (set) => async (next: string) => set(next), +); diff --git a/src/atom.ts b/src/atom.ts index b4652e4..e951e58 100644 --- a/src/atom.ts +++ b/src/atom.ts @@ -1,37 +1,21 @@ class Atom { - private value: T; - private subscribers: Set<() => void>; - update: (next: T) => void - constructor( - value: T, - update: (set: (next: T) => T) => (next: T) => void - ) { + value: T; + subscribers: Set<() => void>; + constructor(value: T) { this.value = value; this.subscribers = new Set(); - - this.update = update(this.set) - } subscribe(callback: () => void) { this.subscribers.add(callback); return () => this.subscribers.delete(callback); } - getSubscribers(){ - return this.subscribers - } - - set(next: T){ - this.value = next - this.subscribers.forEach((callback) => callback()) - return this.value + getSubscribers() { + return this.subscribers; } getState() { - return { - value: this.value, - setState: this.update - } + return this.value; } } diff --git a/src/react.ts b/src/react.ts index 6e26983..70733d5 100644 --- a/src/react.ts +++ b/src/react.ts @@ -2,8 +2,7 @@ import { useMemo, useSyncExternalStore } from "react"; import useSyncExports from "use-sync-external-store/shim/with-selector.js"; import { Atom } from "./atom.ts"; import { Store } from "./store.ts"; -import { StoreApi, Listener } from "./types.ts"; - +import { Listener, StoreApi } from "./types.ts"; const { useSyncExternalStoreWithSelector } = useSyncExports; @@ -42,62 +41,54 @@ const createImpl = >(store: Store, init: T) => { export const useAtom = ( atom: T, - update: (set: (next: T) => T) => (next: T) => T | Promise + update: (set: (next: T) => T) => (next: T) => T | Promise, ) => { - const atomStore = useMemo( - () => - new Atom(atom, update), - [atom, update], - ); + const atomStore = useMemo(() => new Atom(atom), [atom]); + + const set = (next: T) => { + atomStore.value = next; + atomStore.subscribers.forEach((callback) => callback()); + return next; + }; - const setUpdate = update(atomStore.set) - + const setUpdate = update(set); return [ useSyncExternalStore( (callback) => atomStore.subscribe(callback), () => atomStore.getState(), () => atomStore.getState(), - ).value, - setUpdate - ] as [ - T, - typeof atomStore.update - ] + ), + setUpdate, + ] as [T, typeof setUpdate]; }; const createAtomImpl = ( - atom: T, - update: (set: (next: T) => T) => (next: T) => T | Promise + atomStore: Atom, + update: (next: T) => T | Promise, ) => { - - - const atomStore = new Atom(atom, update); - const init = atomStore.getState() + const init = atomStore.getState(); const useCreatedStore = ( - selector: ({ - value, - setState - }: { - value: T, - setState: typeof atomStore.update - }) => U, + selector: (value: T) => U, comparator?: ({ next, prev }: { next: U; prev: U }) => boolean, ) => { - return useSyncExternalStoreWithSelector( - (callback: Listener) => atomStore.subscribe(callback), - () => atomStore.getState(), - () => init, - selector, - comparator - ? (a: U, b: U) => - comparator({ - next: a, - prev: b, - }) - : undefined, - ); + return [ + useSyncExternalStoreWithSelector( + (callback: Listener) => atomStore.subscribe(callback), + () => atomStore.getState(), + () => init, + selector, + comparator + ? (a: U, b: U) => + comparator({ + next: a, + prev: b, + }) + : undefined, + ), + update, + ] as [U, typeof update]; }; return useCreatedStore; @@ -105,10 +96,18 @@ const createAtomImpl = ( export const atom = ( atom: T, - update: (set: (next: T) => T) => (next: T) => T | Promise + update: (set: (next: T) => T) => (next: T) => T | Promise, ) => { - - return createAtomImpl(atom, update); + const atomStore = new Atom(atom); + + const set = (next: T) => { + atomStore.value = next; + atomStore.subscribers.forEach((callback) => callback()); + return next; + }; + + const assembledUpdate = update(set); + return createAtomImpl(atomStore, assembledUpdate); }; export const create = >(init: T) => { diff --git a/src/store.ts b/src/store.ts index bdde4e2..e19653e 100644 --- a/src/store.ts +++ b/src/store.ts @@ -6,14 +6,12 @@ import { StoreKey, StoreMutations, StoreValue, -} from "./types"; - -const isAsync = (call: T) => ( - call instanceof AsyncFunction && - AsyncFunction !== Function && - AsyncFunction !== GeneratorFunction -) === true +} from "./types.ts"; +const isAsync = (call: T) => + (call instanceof AsyncFunction && + AsyncFunction !== Function && + AsyncFunction !== GeneratorFunction) === true; class Store> { private state: StoreData; @@ -47,17 +45,15 @@ class Store> { ] as StoreMutations[typeof mutationKey]; this.mutators[mutationKey] = mutator; - if ( - isAsync(mutator) || isAsync(mutator(null as any)) - ) { + if (isAsync(mutator) || isAsync(mutator(null as any))) { const assembledMutation = async (next: StoreValue) => { const { value } = this.assembled[key]; const mutation = this.mutators[mutationKey as unknown as MutationKey]; - const nextVal = await mutation(next)( + const nextVal = (await mutation(next)( value as StoreValue, - ) as typeof value; + )) as typeof value; const update = Object.assign(this.assembled[key], { value: nextVal, @@ -87,7 +83,7 @@ class Store> { const update = Object.assign(this.assembled[key], { value: nextVal, }); - + this.assembled = Object.assign({}, this.assembled, update); this.subscribers.forEach((callback) => callback()); diff --git a/src/types.ts b/src/types.ts index 2dad4d7..175a242 100644 --- a/src/types.ts +++ b/src/types.ts @@ -75,11 +75,12 @@ export type StoreMutations> = Record< > >; - - export type AtomStore = { - value: T[Exclude]; - update: AtomMutation], T[Exclude]> + value: T[Exclude]; + update: AtomMutation< + T[Exclude], + T[Exclude] + >; }; export type MutationRequest< @@ -93,16 +94,11 @@ export type MutationRequest< export type Listener = () => void; - -export type AtomMutation< - V, - P, -> = P extends (set: (next: V) => V) => (next: V) => V - ? (set: (next: V) => V) => (next: V) => V - : ( - (set: (next: V) => V) => (next: V) => Promise - ) - +export type AtomMutation = P extends ( + set: (next: V) => V, +) => (next: V) => V + ? (set: (next: V) => V) => (next: V) => V + : (set: (next: V) => V) => (next: V) => Promise; export type Mutation< T extends StoreApi, @@ -113,12 +109,13 @@ export type Mutation< (P extends ( set: (state: StoreValue) => StoreValue, ) => (next: StoreValue) => StoreValue - ? (set: (state: StoreValue) => StoreValue) => (next: StoreValue) => StoreValue + ? ( + set: (state: StoreValue) => StoreValue, + ) => (next: StoreValue) => StoreValue : ( - set: (state: StoreValue) => StoreValue + set: (state: StoreValue) => StoreValue, ) => (next: StoreValue) => Promise>); - export type AssembledMutation< T extends StoreApi, K extends keyof T, @@ -127,4 +124,4 @@ export type AssembledMutation< > = StoreApi[K][M] & (P extends (next: StoreValue) => StoreValue ? (next: StoreValue) => StoreValue - : (next: StoreValue) => Promise>); \ No newline at end of file + : (next: StoreValue) => Promise>); diff --git a/tsconfig.json b/tsconfig.json index de279d5..20e453c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,7 @@ "allowImportingTsExtensions": true, "moduleResolution": "bundler", "noUncheckedIndexedAccess": true, - "baseUrl": ".", + "baseUrl": "." }, "include": ["src"], "exclude": ["node_modules", "dist"]