Skip to content

Commit

Permalink
Merge branch 'main' into fix-unwrap-setself-regression
Browse files Browse the repository at this point in the history
  • Loading branch information
dai-shi authored Nov 11, 2024
2 parents 2fbac34 + c883763 commit 8ac1645
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 291 deletions.
100 changes: 51 additions & 49 deletions src/vanilla/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,21 @@ const flushPending = (pending: Pending) => {
}
}

type GetAtomState = <Value>(
atom: Atom<Value>,
originAtomState?: AtomState,
) => AtomState<Value>

// internal & unstable type
type StoreArgs = readonly [
getAtomState: GetAtomState,
// possible other arguments in the future
getAtomState: <Value>(atom: Atom<Value>) => AtomState<Value>,
atomRead: <Value>(
atom: Atom<Value>,
...params: Parameters<Atom<Value>['read']>
) => Value,
atomWrite: <Value, Args extends unknown[], Result>(
atom: WritableAtom<Value, Args, Result>,
...params: Parameters<WritableAtom<Value, Args, Result>['write']>
) => Result,
atomOnMount: <Value, Args extends unknown[], Result>(
atom: WritableAtom<Value, Args, Result>,
setAtom: (...args: Args) => Result,
) => OnUnmount | void,
]

// for debugging purpose only
Expand All @@ -245,7 +251,9 @@ type Store = PrdStore | (PrdStore & DevStoreRev4)
export type INTERNAL_DevStoreRev4 = DevStoreRev4
export type INTERNAL_PrdStore = PrdStore

const buildStore = (getAtomState: StoreArgs[0]): Store => {
const buildStore = (
...[getAtomState, atomRead, atomWrite, atomOnMount]: StoreArgs
): Store => {
// for debugging purpose only
let debugMountedAtoms: Set<AnyAtom>

Expand All @@ -264,11 +272,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
if (isPromiseLike(valueOrPromise)) {
patchPromiseForCancelability(valueOrPromise)
for (const a of atomState.d.keys()) {
addPendingPromiseToDependency(
atom,
valueOrPromise,
getAtomState(a, atomState),
)
addPendingPromiseToDependency(atom, valueOrPromise, getAtomState(a))
}
atomState.v = valueOrPromise
delete atomState.e
Expand All @@ -287,9 +291,9 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
const readAtomState = <Value>(
pending: Pending | undefined,
atom: Atom<Value>,
atomState: AtomState<Value>,
dirtyAtoms?: Set<AnyAtom>,
): AtomState<Value> => {
const atomState = getAtomState(atom)
// See if we can skip recomputing this atom.
if (isAtomStateInitialized(atomState)) {
// If the atom is mounted, we can use cached atom state.
Expand All @@ -305,8 +309,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
([a, n]) =>
// Recursively, read the atom state of the dependency, and
// check if the atom epoch number is unchanged
readAtomState(pending, a, getAtomState(a, atomState), dirtyAtoms)
.n === n,
readAtomState(pending, a, dirtyAtoms).n === n,
)
) {
return atomState
Expand All @@ -317,7 +320,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
let isSync = true
const getter: Getter = <V>(a: Atom<V>) => {
if (isSelfAtom(atom, a)) {
const aState = getAtomState(a, atomState)
const aState = getAtomState(a)
if (!isAtomStateInitialized(aState)) {
if (hasInitialValue(a)) {
setAtomStateValueOrPromise(a, aState, a.init)
Expand All @@ -329,12 +332,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
return returnAtomValue(aState)
}
// a !== atom
const aState = readAtomState(
pending,
a,
getAtomState(a, atomState),
dirtyAtoms,
)
const aState = readAtomState(pending, a, dirtyAtoms)
if (isSync) {
addDependency(pending, atom, atomState, a, aState)
} else {
Expand Down Expand Up @@ -375,7 +373,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
},
}
try {
const valueOrPromise = atom.read(getter, options as never)
const valueOrPromise = atomRead(atom, getter, options as never)
setAtomStateValueOrPromise(atom, atomState, valueOrPromise)
if (isPromiseLike(valueOrPromise)) {
valueOrPromise.onCancel?.(() => controller?.abort())
Expand All @@ -400,7 +398,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
}

const readAtom = <Value>(atom: Atom<Value>): Value =>
returnAtomValue(readAtomState(undefined, atom, getAtomState(atom)))
returnAtomValue(readAtomState(undefined, atom))

const getDependents = <Value>(
pending: Pending,
Expand All @@ -409,16 +407,16 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
): Map<AnyAtom, AtomState> => {
const dependents = new Map<AnyAtom, AtomState>()
for (const a of atomState.m?.t || []) {
dependents.set(a, getAtomState(a, atomState))
dependents.set(a, getAtomState(a))
}
for (const atomWithPendingPromise of atomState.p) {
dependents.set(
atomWithPendingPromise,
getAtomState(atomWithPendingPromise, atomState),
getAtomState(atomWithPendingPromise),
)
}
getPendingDependents(pending, atom)?.forEach((dependent) => {
dependents.set(dependent, getAtomState(dependent, atomState))
dependents.set(dependent, getAtomState(dependent))
})
return dependents
}
Expand Down Expand Up @@ -471,7 +469,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
}
}
if (hasChangedDeps) {
readAtomState(pending, a, aState, markedAtoms)
readAtomState(pending, a, markedAtoms)
mountDependencies(pending, a, aState)
if (prevEpochNumber !== aState.n) {
addPendingAtom(pending, a, aState)
Expand All @@ -485,16 +483,15 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
const writeAtomState = <Value, Args extends unknown[], Result>(
pending: Pending,
atom: WritableAtom<Value, Args, Result>,
atomState: AtomState<Value>,
...args: Args
): Result => {
const getter: Getter = <V>(a: Atom<V>) =>
returnAtomValue(readAtomState(pending, a, getAtomState(a, atomState)))
returnAtomValue(readAtomState(pending, a))
const setter: Setter = <V, As extends unknown[], R>(
a: WritableAtom<V, As, R>,
...args: As
) => {
const aState = getAtomState(a, atomState)
const aState = getAtomState(a)
let r: R | undefined
if (isSelfAtom(atom, a)) {
if (!hasInitialValue(a)) {
Expand All @@ -511,12 +508,12 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
recomputeDependents(pending, a, aState)
}
} else {
r = writeAtomState(pending, a, aState, ...args) as R
r = writeAtomState(pending, a, ...args) as R
}
flushPending(pending)
return r as R
}
const result = atom.write(getter, setter, ...args)
const result = atomWrite(atom, getter, setter, ...args)
return result
}

Expand All @@ -525,7 +522,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
...args: Args
): Result => {
const pending = createPending()
const result = writeAtomState(pending, atom, getAtomState(atom), ...args)
const result = writeAtomState(pending, atom, ...args)
flushPending(pending)
return result
}
Expand All @@ -538,15 +535,15 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
if (atomState.m && !isPendingPromise(atomState.v)) {
for (const a of atomState.d.keys()) {
if (!atomState.m.d.has(a)) {
const aMounted = mountAtom(pending, a, getAtomState(a, atomState))
const aMounted = mountAtom(pending, a, getAtomState(a))
aMounted.t.add(atom)
atomState.m.d.add(a)
}
}
for (const a of atomState.m.d || []) {
if (!atomState.d.has(a)) {
atomState.m.d.delete(a)
const aMounted = unmountAtom(pending, a, getAtomState(a, atomState))
const aMounted = unmountAtom(pending, a, getAtomState(a))
aMounted?.t.delete(atom)
}
}
Expand All @@ -560,10 +557,10 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
): Mounted => {
if (!atomState.m) {
// recompute atom state
readAtomState(pending, atom, atomState)
readAtomState(pending, atom)
// mount dependencies first
for (const a of atomState.d.keys()) {
const aMounted = mountAtom(pending, a, getAtomState(a, atomState))
const aMounted = mountAtom(pending, a, getAtomState(a))
aMounted.t.add(atom)
}
// mount self
Expand All @@ -575,12 +572,11 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
if (import.meta.env?.MODE !== 'production') {
debugMountedAtoms.add(atom)
}
if (isActuallyWritableAtom(atom) && atom.onMount) {
if (isActuallyWritableAtom(atom)) {
const mounted = atomState.m
const { onMount } = atom
addPendingFunction(pending, () => {
const onUnmount = onMount((...args) =>
writeAtomState(pending, atom, atomState, ...args),
const onUnmount = atomOnMount(atom, (...args) =>
writeAtomState(pending, atom, ...args),
)
if (onUnmount) {
mounted.u = onUnmount
Expand All @@ -599,9 +595,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
if (
atomState.m &&
!atomState.m.l.size &&
!Array.from(atomState.m.t).some((a) =>
getAtomState(a, atomState).m?.d.has(atom),
)
!Array.from(atomState.m.t).some((a) => getAtomState(a).m?.d.has(atom))
) {
// unmount self
const onUnmount = atomState.m.u
Expand All @@ -614,7 +608,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
}
// unmount dependencies
for (const a of atomState.d.keys()) {
const aMounted = unmountAtom(pending, a, getAtomState(a, atomState))
const aMounted = unmountAtom(pending, a, getAtomState(a))
aMounted?.t.delete(atom)
}
return undefined
Expand All @@ -638,7 +632,7 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
}

const unstable_derive = (fn: (...args: StoreArgs) => StoreArgs) =>
buildStore(...fn(getAtomState))
buildStore(...fn(getAtomState, atomRead, atomWrite, atomOnMount))

const store: Store = {
get: readAtom,
Expand Down Expand Up @@ -686,14 +680,22 @@ const buildStore = (getAtomState: StoreArgs[0]): Store => {
export const createStore = (): Store => {
const atomStateMap = new WeakMap()
const getAtomState = <Value>(atom: Atom<Value>) => {
if (import.meta.env?.MODE !== 'production' && !atom) {
throw new Error('Atom is undefined or null')
}
let atomState = atomStateMap.get(atom) as AtomState<Value> | undefined
if (!atomState) {
atomState = { d: new Map(), p: new Set(), n: 0 }
atomStateMap.set(atom, atomState)
}
return atomState
}
return buildStore(getAtomState)
return buildStore(
getAtomState,
(atom, ...params) => atom.read(...params),
(atom, ...params) => atom.write(...params),
(atom, ...params) => atom.onMount?.(...params),
)
}

let defaultStore: Store | undefined
Expand Down
21 changes: 11 additions & 10 deletions src/vanilla/utils/loadable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,17 @@ export function loadable<Value>(anAtom: Atom<Value>): Atom<Loadable<Value>> {
if (cached1) {
return cached1
}
promise
.then(
(data) => {
loadableCache.set(promise, { state: 'hasData', data })
},
(error) => {
loadableCache.set(promise, { state: 'hasError', error })
},
)
.finally(setSelf)
promise.then(
(data) => {
loadableCache.set(promise, { state: 'hasData', data })
setSelf()
},
(error) => {
loadableCache.set(promise, { state: 'hasError', error })
setSelf()
},
)

const cached2 = loadableCache.get(promise)
if (cached2) {
return cached2
Expand Down
Loading

0 comments on commit 8ac1645

Please sign in to comment.