Skip to content

Commit

Permalink
more changes still wip
Browse files Browse the repository at this point in the history
  • Loading branch information
David Maskasky committed Sep 24, 2024
1 parent 914d987 commit 5bc54a3
Show file tree
Hide file tree
Showing 12 changed files with 721 additions and 333 deletions.
2 changes: 1 addition & 1 deletion __tests__/derive/understanding/atomState.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AtomState } from '../../../jotai/vanilla/store'
import { AtomState } from 'src/ScopeProvider2/types'
import { atom, createStore } from '../../../jotai'
import { assertIsDevStore } from '../../utils'

Expand Down
269 changes: 259 additions & 10 deletions __tests__/derive/understanding/unstable_derive.test.ts

Large diffs are not rendered by default.

36 changes: 1 addition & 35 deletions __tests__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,7 @@ export function clickButton(container: HTMLElement, querySelector: string) {
fireEvent.click(button)
}

export function getDevStore(store: Store): INTERNAL_PrdStore & INTERNAL_DevStoreRev4 {
if (!isDevStore(store)) {
throw new Error('Store is not a dev store')
}
return store
}

export function isDevStore(store: Store): store is INTERNAL_PrdStore & INTERNAL_DevStoreRev4 {
function isDevStore(store: Store): store is INTERNAL_PrdStore & INTERNAL_DevStoreRev4 {
return (
'dev4_get_internal_weak_map' in store &&
'dev4_get_mounted_atoms' in store &&
Expand All @@ -50,30 +43,3 @@ export function assertIsDevStore(
export function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms))
}

export function getDevStore(store: Store): INTERNAL_PrdStore & INTERNAL_DevStoreRev4 {
if (!isDevStore(store)) {
throw new Error('Store is not a dev store')
}
return store
}

export function isDevStore(store: Store): store is INTERNAL_PrdStore & INTERNAL_DevStoreRev4 {
return (
'dev4_get_internal_weak_map' in store &&
'dev4_get_mounted_atoms' in store &&
'dev4_restore_atoms' in store
)
}

export function assertIsDevStore(
store: Store,
): asserts store is INTERNAL_PrdStore & INTERNAL_DevStoreRev4 {
if (!isDevStore(store)) {
throw new Error('Store is not a dev store')
}
}

export function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
31 changes: 15 additions & 16 deletions approaches/unstable_derive.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Objectives

1. Derived atoms are not copied if they don’t depend on scoped atoms.
2. When a derived atom starts depending on a scoped atom, it's state is copied to the scope state.
a. If the derived atom has already mounted, don't call onMount again.
2. When a derived atom starts depending on a scoped atom, a new atom state is created as the scoped atom state.
3. When a derived atom stops depending on a scoped atom, it must be removed from the scope state and restored to the original atom state.
a. When changing between scoped and unscoped, all subscibers must be notified.

Expand All @@ -21,33 +20,33 @@
A computed atom may or may not consume scoped atoms. This may also change as state changes.

```tsx
const providerAtom = atom('unscoped');
const scopedProviderAtom = atom('scoped');
const shouldConsumeScopedAtom = atom(false);
const providerAtom = atom('unscoped')
const scopedProviderAtom = atom('scoped')
const shouldConsumeScopedAtom = atom(false)
const consumerAtom = atom((get) => {
if (get(shouldConsumeScopedAtom)) {
return get(scopedProviderAtom);
return get(scopedProviderAtom)
}
return get(providerAtom);
});
return get(providerAtom)
})

function Component() {
const value = useAtomValue(consumerAtom);
return value;
const value = useAtomValue(consumerAtom)
return value
}

function App() {
const setShouldConsumeScopedAtom = useSetAtom(shouldConsumeScopedAtom);
const setShouldConsumeScopedAtom = useSetAtom(shouldConsumeScopedAtom)
useEffect(() => {
const timeoutId = setTimeout(setShouldConsumeScopedAtom, 1000, true);
return () => clearTimeout(timeoutId);
}, []);
const timeoutId = setTimeout(setShouldConsumeScopedAtom, 1000, true)
return () => clearTimeout(timeoutId)
}, [])

return (
<ScopeProvider atoms={[scopedProviderAtom]}>
<Component />
</ScopeProvider>
);
)
}
```

Expand Down Expand Up @@ -78,5 +77,5 @@ All computed atoms (`atom.read !== defaultRead`) will have their base atomState
0. Update all computed atoms with a proxy state in the parent store.
1. If a computer atom does not depend on any scoped atoms, remove it from the unscopedComputed set
2. If a computed atom starts depending on a scoped atom, add it to the scopedComputed set.
a. If the scoped state does not already exist, copy the value from the original atom state.
a. If the scoped state does not already exist, create a new scoped atom state.
3. If a computed atom stops depending on a scoped atom, remove it from the scopedComputed set.
2 changes: 1 addition & 1 deletion jotai/vanilla/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ type Mounted = {
* Mutable atom state,
* tracked for both mounted and unmounted atoms in a store.
*/
export type AtomState<Value = AnyValue> = {
type AtomState<Value = AnyValue> = {
/**
* Map of atoms that the atom depends on.
* The map value is the epoch number of the dependency.
Expand Down
27 changes: 27 additions & 0 deletions notes
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
computed atoms are like implicit atoms but reverse
computed atoms are not copied
reverseImplicitSet is a set of computed atoms to indicate whether a computed atom should be treated as a reverse implicit
the atom is removed from the set between recomputations
by intercepting the readFn, if an atom is `get` that is either an explicit or reverse implicit,

- then the atom is added to the reverse implicit set

only the readFn determines if the atom is added to the reverse implicit set
intercepting the readFn and writeFn is used to get the "correct" atom
when a computed atom converts to reverse implicit,

- its atomState is created from scratch
- this is because the atomState stores a different value for the scoped atom and can have different dependencies

**Special Case:** on first read, when a computed atom reads a scoped atom,

1. it is added to the reverse implicit set
1. the atomState is copied from the unscoped atomState
1. getAtomState points to the scoped atomState

the atomStateProxy is no longer needed.

# Implementation

readAtomTrap:
getter:
16 changes: 16 additions & 0 deletions src/ScopeProvider/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export function createScope(
* Returns a scoped atom from the original atom.
* @param anAtom
* @param implicitScope the atom is implicitly scoped in the provided scope
* - when the implicit scope is the current scope, the atom is emplaced in the implicit set and returned
* @returns the scoped atom and the scope of the atom
*/
function getAtom<T extends AnyAtom>(anAtom: T, implicitScope?: Scope): [T, Scope?] {
Expand Down Expand Up @@ -152,6 +153,9 @@ export function createScope(
}

/**
* Makes a clone of the atom
* - replaces read with a scoped read function
* - replaces write with a scoped write function
* @returns a scoped copy of the atom
*/
function cloneAtom<T>(originalAtom: Atom<T>, implicitScope?: Scope) {
Expand Down Expand Up @@ -179,6 +183,12 @@ export function createScope(
return scopedAtom
}

/**
* Creates a scoped read function that intercepts the read function of the original atom
* to intercept the getter with the custom getAtom function
* @param implicitScope
* @returns
*/
function createScopedRead<T extends Atom<unknown>>(
read: T['read'],
implicitScope?: Scope,
Expand All @@ -194,6 +204,12 @@ export function createScope(
}
}

/**
* Creates a scoped write function that intercepts the write function of the original atom
* to intercept the getter and setter with the custom getAtom function
* @param implicitScope
* @returns
*/
function createScopedWrite<T extends AnyWritableAtom>(
write: T['write'],
implicitScope?: Scope,
Expand Down
Loading

0 comments on commit 5bc54a3

Please sign in to comment.