Skip to content

Commit

Permalink
AL: fix atom set state
Browse files Browse the repository at this point in the history
  • Loading branch information
adalundhe committed Feb 25, 2024
1 parent bb496da commit 46eb176
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 115 deletions.
23 changes: 7 additions & 16 deletions examples/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,16 @@ const { beep } = useMyCustomStore(

const useMyAtom = atom<typeof beep>(
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)
)
beep,
(set) => async (next: string) => set(next),
);
28 changes: 6 additions & 22 deletions src/atom.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,21 @@
class Atom<T> {
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;
}
}

Expand Down
89 changes: 44 additions & 45 deletions src/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -42,73 +41,73 @@ const createImpl = <T extends StoreApi<T>>(store: Store<T>, init: T) => {

export const useAtom = <T>(
atom: T,
update: (set: (next: T) => T) => (next: T) => T | Promise<T>
update: (set: (next: T) => T) => (next: T) => T | Promise<T>,
) => {
const atomStore = useMemo(
() =>
new Atom<T>(atom, update),
[atom, update],
);
const atomStore = useMemo(() => new Atom<T>(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 = <T>(
atom: T,
update: (set: (next: T) => T) => (next: T) => T | Promise<T>
atomStore: Atom<T>,
update: (next: T) => T | Promise<T>,
) => {


const atomStore = new Atom<T>(atom, update);
const init = atomStore.getState()
const init = atomStore.getState();

const useCreatedStore = <U>(
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;
};

export const atom = <T>(
atom: T,
update: (set: (next: T) => T) => (next: T) => T | Promise<T>
update: (set: (next: T) => T) => (next: T) => T | Promise<T>,
) => {

return createAtomImpl(atom, update);
const atomStore = new Atom<T>(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 = <T extends StoreApi<T>>(init: T) => {
Expand Down
22 changes: 9 additions & 13 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ import {
StoreKey,
StoreMutations,
StoreValue,
} from "./types";

const isAsync = <T>(call: T) => (
call instanceof AsyncFunction &&
AsyncFunction !== Function &&
AsyncFunction !== GeneratorFunction
) === true
} from "./types.ts";

const isAsync = <T>(call: T) =>
(call instanceof AsyncFunction &&
AsyncFunction !== Function &&
AsyncFunction !== GeneratorFunction) === true;

class Store<T extends StoreApi<T>> {
private state: StoreData<T>;
Expand Down Expand Up @@ -47,17 +45,15 @@ class Store<T extends StoreApi<T>> {
] as StoreMutations<T>[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<T, keyof T>) => {
const { value } = this.assembled[key];
const mutation =
this.mutators[mutationKey as unknown as MutationKey<T>];

const nextVal = await mutation(next)(
const nextVal = (await mutation(next)(
value as StoreValue<T, keyof T>,
) as typeof value;
)) as typeof value;

const update = Object.assign(this.assembled[key], {
value: nextVal,
Expand Down Expand Up @@ -87,7 +83,7 @@ class Store<T extends StoreApi<T>> {
const update = Object.assign(this.assembled[key], {
value: nextVal,
});

this.assembled = Object.assign({}, this.assembled, update);

this.subscribers.forEach((callback) => callback());
Expand Down
33 changes: 15 additions & 18 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@ export type StoreMutations<T extends StoreApi<T>> = Record<
>
>;



export type AtomStore<T> = {
value: T[Exclude<keyof T, 'update'>];
update: AtomMutation<T[Exclude<keyof T, 'update'>], T[Exclude<keyof T, 'value'>]>
value: T[Exclude<keyof T, "update">];
update: AtomMutation<
T[Exclude<keyof T, "update">],
T[Exclude<keyof T, "value">]
>;
};

export type MutationRequest<
Expand All @@ -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<V>
)

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<V>;

export type Mutation<
T extends StoreApi<T>,
Expand All @@ -113,12 +109,13 @@ export type Mutation<
(P extends (
set: (state: StoreValue<T, K>) => StoreValue<T, K>,
) => (next: StoreValue<T, K>) => StoreValue<T, K>
? (set: (state: StoreValue<T, K>) => StoreValue<T, K>) => (next: StoreValue<T, K>) => StoreValue<T, K>
? (
set: (state: StoreValue<T, K>) => StoreValue<T, K>,
) => (next: StoreValue<T, K>) => StoreValue<T, K>
: (
set: (state: StoreValue<T, K>) => StoreValue<T, K>
set: (state: StoreValue<T, K>) => StoreValue<T, K>,
) => (next: StoreValue<T, K>) => Promise<StoreValue<T, K>>);


export type AssembledMutation<
T extends StoreApi<T>,
K extends keyof T,
Expand All @@ -127,4 +124,4 @@ export type AssembledMutation<
> = StoreApi<T>[K][M] &
(P extends (next: StoreValue<T, K>) => StoreValue<T, K>
? (next: StoreValue<T, K>) => StoreValue<T, K>
: (next: StoreValue<T, K>) => Promise<StoreValue<T, K>>);
: (next: StoreValue<T, K>) => Promise<StoreValue<T, K>>);
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"allowImportingTsExtensions": true,
"moduleResolution": "bundler",
"noUncheckedIndexedAccess": true,
"baseUrl": ".",
"baseUrl": "."
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
Expand Down

0 comments on commit 46eb176

Please sign in to comment.