Replace Atom.setIn() and Atom.reduceIn() with the type-safe Atom.setByPointer() and Atom.reduceByPointer()

This commit is contained in:
Aria Minaei 2022-12-01 17:54:14 +00:00
parent 5b6306bde9
commit ac9d8b4481
15 changed files with 82 additions and 223 deletions

View file

@ -4,10 +4,10 @@ import last from 'lodash-es/last'
import type {Prism} from './prism/Interface' import type {Prism} from './prism/Interface'
import {isPrism} from './prism/Interface' import {isPrism} from './prism/Interface'
import type {Pointer, PointerType} from './pointer' import type {Pointer, PointerType} from './pointer'
import {getPointerParts} from './pointer'
import {isPointer} from './pointer' import {isPointer} from './pointer'
import pointer, {getPointerMeta} from './pointer' import pointer, {getPointerMeta} from './pointer'
import type {$FixMe, $IntentionalAny} from './types' import type {$FixMe, $IntentionalAny} from './types'
import type {PathBasedReducer} from './utils/PathBasedReducer'
import updateDeep from './utils/updateDeep' import updateDeep from './utils/updateDeep'
import prism from './prism/prism' import prism from './prism/prism'
@ -176,39 +176,22 @@ export default class Atom<State> implements IdentityPrismProvider {
return path.length === 0 ? this.getState() : get(this.getState(), path) return path.length === 0 ? this.getState() : get(this.getState(), path)
} }
/** reduce(fn: (state: State) => State) {
* Creates a new state object from the current one, where the value at `path` this.set(fn(this.get()))
* is replaced by the return value of `reducer`, then sets it.
*
* @remarks
* Doesn't mutate the old state, and preserves referential equality between
* values of the old state and the new state where possible.
*
* @example
* ```ts
* someAtom.getIn(['a']) // 1
* someAtom.reduceState(['a'], (state) => state + 1);
* someAtom.getIn(['a']) // 2
* ```
*
* @param path - The path to call the reducer at.
* @param reducer - The function to use for creating the new state.
*/
// TODO: Why is this a property and not a method?
reduceState: PathBasedReducer<State, State> = (
path: $IntentionalAny[],
reducer: $IntentionalAny,
) => {
const newState = updateDeep(this.getState(), path, reducer)
this.setState(newState)
return newState
} }
/** reduceByPointer<S>(
* Sets the state of the atom at `path`. fn: (p: Pointer<State>) => Pointer<S>,
*/ reducer: (s: S) => S,
setIn(path: $FixMe[], val: $FixMe) { ) {
return this.reduceState(path, () => val) const pointer = fn(this.pointer)
const path = getPointerParts(pointer).path
const newState = updateDeep(this.get(), path, reducer)
this.set(newState)
}
setByPointer<S>(fn: (p: Pointer<State>) => Pointer<S>, val: S) {
this.reduceByPointer(fn, () => val)
} }
private _checkUpdates(scope: Scope, oldState: unknown, newState: unknown) { private _checkUpdates(scope: Scope, oldState: unknown, newState: unknown) {

View file

@ -6,16 +6,16 @@ import iterateOver from './iterateOver'
describe(`iterateOver()`, () => { describe(`iterateOver()`, () => {
test('it should work', () => { test('it should work', () => {
const a = new Atom({a: 0}) const a = new Atom(0)
let iter = iterateOver(a.pointer.a) let iter = iterateOver(a.pointer)
expect(iter.next().value).toEqual(0) expect(iter.next().value).toEqual(0)
a.setIn(['a'], 1) a.set(1)
a.setIn(['a'], 2) a.set(2)
expect(iter.next()).toMatchObject({value: 2, done: false}) expect(iter.next()).toMatchObject({value: 2, done: false})
iter.return() iter.return()
iter = iterateOver(a.pointer.a) iter = iterateOver(a.pointer)
expect(iter.next().value).toEqual(2) expect(iter.next().value).toEqual(2)
a.setIn(['a'], 3) a.set(3)
expect(iter.next()).toMatchObject({done: false, value: 3}) expect(iter.next()).toMatchObject({done: false, value: 3})
iter.return() iter.return()
}) })

View file

@ -25,7 +25,7 @@ describe('prism', () => {
changes.push(c) changes.push(c)
}) })
o.reduceState(['foo'], () => 'foo2') o.reduce(({foo}) => ({foo: 'foo2'}))
ticker.tick() ticker.tick()
expect(changes).toMatchObject(['foo2boo']) expect(changes).toMatchObject(['foo2boo'])
}) })
@ -43,11 +43,11 @@ describe('prism', () => {
describe('prism.ref()', () => { describe('prism.ref()', () => {
it('should work', () => { it('should work', () => {
const theAtom: Atom<{n: number}> = new Atom({n: 2}) const theAtom: Atom<number> = new Atom(2)
const isEvenD = prism((): {isEven: boolean} => { const isEvenD = prism((): {isEven: boolean} => {
const ref = prism.ref<{isEven: boolean} | undefined>('cache', undefined) const ref = prism.ref<{isEven: boolean} | undefined>('cache', undefined)
const currentN = val(theAtom.pointer.n) const currentN = val(theAtom.pointer)
const isEven = currentN % 2 === 0 const isEven = currentN % 2 === 0
if (ref.current && ref.current.isEven === isEven) { if (ref.current && ref.current.isEven === isEven) {
@ -60,20 +60,20 @@ describe('prism', () => {
const iterator = iterateAndCountTicks(isEvenD) const iterator = iterateAndCountTicks(isEvenD)
theAtom.reduceState(['n'], () => 3) theAtom.reduce(() => 3)
expect(iterator.next().value).toMatchObject({ expect(iterator.next().value).toMatchObject({
value: {isEven: false}, value: {isEven: false},
ticks: 0, ticks: 0,
}) })
theAtom.reduceState(['n'], () => 5) theAtom.reduce(() => 5)
theAtom.reduceState(['n'], () => 7) theAtom.reduce(() => 7)
expect(iterator.next().value).toMatchObject({ expect(iterator.next().value).toMatchObject({
value: {isEven: false}, value: {isEven: false},
ticks: 1, ticks: 1,
}) })
theAtom.reduceState(['n'], () => 2) theAtom.reduce(() => 2)
theAtom.reduceState(['n'], () => 4) theAtom.reduce(() => 4)
expect(iterator.next().value).toMatchObject({ expect(iterator.next().value).toMatchObject({
value: {isEven: true}, value: {isEven: true},
ticks: 1, ticks: 1,
@ -91,10 +91,10 @@ describe('prism', () => {
const sequence: unknown[] = [] const sequence: unknown[] = []
let deps: unknown[] = [] let deps: unknown[] = []
const a = new Atom({letter: 'a'}) const a = new Atom('a')
const prsm = prism(() => { const prsm = prism(() => {
const n = val(a.pointer.letter) const n = val(a.pointer)
const iterationAtTimeOfCall = iteration const iterationAtTimeOfCall = iteration
sequence.push({prismCall: iterationAtTimeOfCall}) sequence.push({prismCall: iterationAtTimeOfCall})
@ -120,14 +120,14 @@ describe('prism', () => {
sequence.length = 0 sequence.length = 0
iteration++ iteration++
a.setIn(['letter'], 'b') a.set('b')
ticker.tick() ticker.tick()
expect(sequence).toMatchObject([{prismCall: 1}, {change: 'b'}]) expect(sequence).toMatchObject([{prismCall: 1}, {change: 'b'}])
sequence.length = 0 sequence.length = 0
deps = [1] deps = [1]
iteration++ iteration++
a.setIn(['letter'], 'c') a.set('c')
ticker.tick() ticker.tick()
expect(sequence).toMatchObject([ expect(sequence).toMatchObject([
{prismCall: 2}, {prismCall: 2},
@ -151,10 +151,10 @@ describe('prism', () => {
const sequence: unknown[] = [] const sequence: unknown[] = []
let deps: unknown[] = [] let deps: unknown[] = []
const a = new Atom({letter: 'a'}) const a = new Atom('a')
const prsm = prism(() => { const prsm = prism(() => {
const n = val(a.pointer.letter) const n = val(a.pointer)
const iterationAtTimeOfCall = iteration const iterationAtTimeOfCall = iteration
sequence.push({prismCall: iterationAtTimeOfCall}) sequence.push({prismCall: iterationAtTimeOfCall})
@ -184,7 +184,7 @@ describe('prism', () => {
sequence.length = 0 sequence.length = 0
iteration++ iteration++
a.setIn(['letter'], 'b') a.set('b')
ticker.tick() ticker.tick()
expect(sequence).toMatchObject([ expect(sequence).toMatchObject([
{prismCall: 1}, {prismCall: 1},
@ -195,7 +195,7 @@ describe('prism', () => {
deps = [1] deps = [1]
iteration++ iteration++
a.setIn(['letter'], 'c') a.set('c')
ticker.tick() ticker.tick()
expect(sequence).toMatchObject([ expect(sequence).toMatchObject([
{prismCall: 2}, {prismCall: 2},

View file

@ -1,133 +0,0 @@
export type PathBasedReducer<S, ReturnType> = {
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
A7 extends keyof S[A0][A1][A2][A3][A4][A5][A6],
A8 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7],
A9 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7][A8],
A10 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9],
>(
addr: [A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9][A10],
) => S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9][A10],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
A7 extends keyof S[A0][A1][A2][A3][A4][A5][A6],
A8 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7],
A9 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7][A8],
>(
addr: [A0, A1, A2, A3, A4, A5, A6, A7, A8, A9],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9],
) => S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
A7 extends keyof S[A0][A1][A2][A3][A4][A5][A6],
A8 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7],
>(
addr: [A0, A1, A2, A3, A4, A5, A6, A7, A8],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6][A7][A8],
) => S[A0][A1][A2][A3][A4][A5][A6][A7][A8],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
A7 extends keyof S[A0][A1][A2][A3][A4][A5][A6],
>(
addr: [A0, A1, A2, A3, A4, A5, A6, A7],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6][A7],
) => S[A0][A1][A2][A3][A4][A5][A6][A7],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
>(
addr: [A0, A1, A2, A3, A4, A5, A6],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6],
) => S[A0][A1][A2][A3][A4][A5][A6],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
>(
addr: [A0, A1, A2, A3, A4, A5],
reducer: (d: S[A0][A1][A2][A3][A4][A5]) => S[A0][A1][A2][A3][A4][A5],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
>(
addr: [A0, A1, A2, A3, A4],
reducer: (d: S[A0][A1][A2][A3][A4]) => S[A0][A1][A2][A3][A4],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
>(
addr: [A0, A1, A2, A3],
reducer: (d: S[A0][A1][A2][A3]) => S[A0][A1][A2][A3],
): ReturnType
<A0 extends keyof S, A1 extends keyof S[A0], A2 extends keyof S[A0][A1]>(
addr: [A0, A1, A2],
reducer: (d: S[A0][A1][A2]) => S[A0][A1][A2],
): ReturnType
<A0 extends keyof S, A1 extends keyof S[A0]>(
addr: [A0, A1],
reducer: (d: S[A0][A1]) => S[A0][A1],
): ReturnType
<A0 extends keyof S>(addr: [A0], reducer: (d: S[A0]) => S[A0]): ReturnType
(addr: undefined[], reducer: (d: S) => S): ReturnType
}

View file

@ -240,11 +240,11 @@ export default class Project {
sheetId: SheetId, sheetId: SheetId,
instanceId: SheetInstanceId = 'default' as SheetInstanceId, instanceId: SheetInstanceId = 'default' as SheetInstanceId,
): Sheet { ): Sheet {
let template = this._sheetTemplates.getState()[sheetId] let template = this._sheetTemplates.get()[sheetId]
if (!template) { if (!template) {
template = new SheetTemplate(this, sheetId) template = new SheetTemplate(this, sheetId)
this._sheetTemplates.setIn([sheetId], template) this._sheetTemplates.reduce((s) => ({...s, [sheetId]: template}))
} }
return template.getInstance(instanceId) return template.getInstance(instanceId)

View file

@ -14,11 +14,11 @@ class ProjectsSingleton {
* We're trusting here that each project id is unique * We're trusting here that each project id is unique
*/ */
add(id: ProjectId, project: Project) { add(id: ProjectId, project: Project) {
this.atom.reduceState(['projects', id], () => project) this.atom.setByPointer((p) => p.projects[id], project)
} }
get(id: ProjectId): Project | undefined { get(id: ProjectId): Project | undefined {
return this.atom.getState().projects[id] return this.atom.get().projects[id]
} }
has(id: ProjectId) { has(id: ProjectId) {

View file

@ -120,11 +120,11 @@ export default class AudioPlaybackController implements IPlaybackController {
} }
private get _playing() { private get _playing() {
return this._state.getState().playing return this._state.get().playing
} }
private set _playing(playing: boolean) { private set _playing(playing: boolean) {
this._state.setIn(['playing'], playing) this._state.setByPointer((p) => p.playing, playing)
} }
destroy() {} destroy() {}
@ -140,11 +140,11 @@ export default class AudioPlaybackController implements IPlaybackController {
} }
private _updatePositionInState(time: number) { private _updatePositionInState(time: number) {
this._state.reduceState(['position'], () => time) this._state.reduce((s) => ({...s, position: time}))
} }
getCurrentPosition() { getCurrentPosition() {
return this._state.getState().position return this._state.get().position
} }
play( play(

View file

@ -66,19 +66,19 @@ export default class DefaultPlaybackController implements IPlaybackController {
} }
private _updatePositionInState(time: number) { private _updatePositionInState(time: number) {
this._state.reduceState(['position'], () => time) this._state.setByPointer((p) => p.position, time)
} }
getCurrentPosition() { getCurrentPosition() {
return this._state.getState().position return this._state.get().position
} }
get playing() { get playing() {
return this._state.getState().playing return this._state.get().playing
} }
set playing(playing: boolean) { set playing(playing: boolean) {
this._state.setIn(['playing'], playing) this._state.setByPointer((p) => p.playing, playing)
} }
play( play(

View file

@ -256,7 +256,11 @@ export default class SheetObject implements IdentityPrismProvider {
const updateSequenceValueFromItsPrism = () => { const updateSequenceValueFromItsPrism = () => {
const triple = pr.getValue() const triple = pr.getValue()
if (!triple) return valsAtom.setIn(pathToProp, undefined) if (!triple)
return valsAtom.setByPointer(
(p) => pointerDeep(p, pathToProp),
undefined,
)
const leftDeserialized = deserializeAndSanitize(triple.left) const leftDeserialized = deserializeAndSanitize(triple.left)
@ -266,7 +270,10 @@ export default class SheetObject implements IdentityPrismProvider {
: leftDeserialized : leftDeserialized
if (triple.right === undefined) if (triple.right === undefined)
return valsAtom.setIn(pathToProp, left) return valsAtom.setByPointer(
(p) => pointerDeep(p, pathToProp),
left,
)
const rightDeserialized = deserializeAndSanitize(triple.right) const rightDeserialized = deserializeAndSanitize(triple.right)
const right = const right =
@ -274,8 +281,8 @@ export default class SheetObject implements IdentityPrismProvider {
? propConfig.default ? propConfig.default
: rightDeserialized : rightDeserialized
return valsAtom.setIn( return valsAtom.setByPointer(
pathToProp, (p) => pointerDeep(p, pathToProp),
interpolate(left, right, triple.progression), interpolate(left, right, triple.progression),
) )
} }

View file

@ -69,7 +69,7 @@ export default class Sheet {
const object = objTemplate.createInstance(this, nativeObject, config) const object = objTemplate.createInstance(this, nativeObject, config)
this._objects.setIn([objectKey], object) this._objects.setByPointer((p) => p[objectKey], object)
return object return object
} }
@ -79,7 +79,7 @@ export default class Sheet {
} }
deleteObject(objectKey: ObjectAddressKey) { deleteObject(objectKey: ObjectAddressKey) {
this._objects.reduceState([], (state) => { this._objects.reduce((state) => {
const newState = {...state} const newState = {...state}
delete newState[objectKey] delete newState[objectKey]
return newState return newState

View file

@ -43,7 +43,7 @@ export default class SheetTemplate {
if (!inst) { if (!inst) {
inst = new Sheet(this, instanceId) inst = new Sheet(this, instanceId)
this._instances.setIn([instanceId], inst) this._instances.setByPointer((p) => p[instanceId], inst)
} }
return inst return inst
@ -65,7 +65,7 @@ export default class SheetTemplate {
config, config,
actions, actions,
) )
this._objectTemplates.setIn([objectKey], template) this._objectTemplates.setByPointer((p) => p[objectKey], template)
} }
return template return template

View file

@ -188,7 +188,7 @@ export class Studio {
setCoreBits(coreBits: CoreBits) { setCoreBits(coreBits: CoreBits) {
this._corePrivateApi = coreBits.privateAPI this._corePrivateApi = coreBits.privateAPI
this._coreAtom.setIn(['core'], coreBits.coreExports) this._coreAtom.setByPointer((p) => p.core, coreBits.coreExports)
this._setProjectsP(coreBits.projectsP) this._setProjectsP(coreBits.projectsP)
} }

View file

@ -49,7 +49,7 @@ let lastLockId = 0
const FrameStampPositionProvider: React.FC<{ const FrameStampPositionProvider: React.FC<{
layoutP: Pointer<SequenceEditorPanelLayout> layoutP: Pointer<SequenceEditorPanelLayout>
}> = ({children, layoutP}) => { }> = ({children, layoutP}) => {
const locksAtom = useMemo(() => new Atom<{list: LockItem[]}>({list: []}), []) const locksAtom = useMemo(() => new Atom<LockItem[]>([]), [])
const currentD = useMemo( const currentD = useMemo(
() => () =>
prism(() => { prism(() => {
@ -57,7 +57,7 @@ const FrameStampPositionProvider: React.FC<{
.memo('p', () => pointerPositionInUnitSpace(layoutP), [layoutP]) .memo('p', () => pointerPositionInUnitSpace(layoutP), [layoutP])
.getValue() .getValue()
const locks = val(locksAtom.pointer.list) const locks = val(locksAtom.pointer)
if (locks.length > 0) { if (locks.length > 0) {
return last(locks)!.position return last(locks)!.position
@ -69,7 +69,7 @@ const FrameStampPositionProvider: React.FC<{
) )
const getLock = useCallback(() => { const getLock = useCallback(() => {
const id = lastLockId++ const id = lastLockId++
locksAtom.reduceState(['list'], (list) => [ locksAtom.reduce((list) => [
...list, ...list,
{ {
id, id,
@ -78,13 +78,11 @@ const FrameStampPositionProvider: React.FC<{
]) ])
const unlock = () => { const unlock = () => {
locksAtom.reduceState(['list'], (list) => locksAtom.reduce((list) => list.filter((lock) => lock.id !== id))
list.filter((lock) => lock.id !== id),
)
} }
const set = (posInUnitSpace: number) => { const set = (posInUnitSpace: number) => {
locksAtom.reduceState(['list'], (list) => { locksAtom.reduce((list) => {
const index = list.findIndex((lock) => lock.id === id) const index = list.findIndex((lock) => lock.id === id)
if (index === -1) { if (index === -1) {
console.warn(`Lock is already freed. This is a bug.`) console.warn(`Lock is already freed. This is a bug.`)

View file

@ -7,6 +7,7 @@ import {prism, pointerToPrism} from '@theatre/dataverse'
import {Atom} from '@theatre/dataverse' import {Atom} from '@theatre/dataverse'
import {usePrismInstance} from '@theatre/react' import {usePrismInstance} from '@theatre/react'
import {selectClosestHTMLAncestor} from '@theatre/studio/utils/selectClosestHTMLAncestor' import {selectClosestHTMLAncestor} from '@theatre/studio/utils/selectClosestHTMLAncestor'
import pointerDeep from '@theatre/shared/utils/pointerDeep'
/** To mean the presence value */ /** To mean the presence value */
export enum PresenceFlag { export enum PresenceFlag {
@ -53,12 +54,15 @@ function createPresenceContext(options: {
flag: rel.flag, flag: rel.flag,
} }
const path = [rel.affects, itemKey, relationId] const path = [rel.affects, itemKey, relationId]
relationsAtom.setIn(path, presence) relationsAtom.setByPointer((p) => pointerDeep(p, path), presence)
return path return path
}) })
return () => { return () => {
for (const pathToUndo of undoAtPaths) { for (const pathToUndo of undoAtPaths) {
relationsAtom.setIn(pathToUndo, undefined) relationsAtom.setByPointer(
(p) => pointerDeep(p, pathToUndo),
undefined,
)
} }
} }
}, },
@ -98,16 +102,16 @@ function createPresenceContext(options: {
return usePrismInstance(focusD) return usePrismInstance(focusD)
}, },
setUserHover(itemKeyOpt) { setUserHover(itemKeyOpt) {
const prev = currentUserHoverItemB.getState() const prev = currentUserHoverItemB.get()
if (prev === itemKeyOpt) { if (prev === itemKeyOpt) {
return return
} }
if (prev) { if (prev) {
currentUserHoverFlagItemsAtom.setIn([prev], false) currentUserHoverFlagItemsAtom.setByPointer((p) => p[prev], false)
} }
currentUserHoverItemB.setState(itemKeyOpt) currentUserHoverItemB.set(itemKeyOpt)
if (itemKeyOpt) { if (itemKeyOpt) {
currentUserHoverFlagItemsAtom.setIn([itemKeyOpt], true) currentUserHoverFlagItemsAtom.setByPointer((p) => p[itemKeyOpt], true)
} }
}, },
} }

View file

@ -27,7 +27,7 @@ export const getMounter = () => {
props: Props, props: Props,
portalNode: HTMLElement, portalNode: HTMLElement,
) { ) {
theAtom.reduceState([], (s) => { theAtom.reduce((s) => {
return { return {
byId: {...s.byId, [id]: {comp, props, portalNode}}, byId: {...s.byId, [id]: {comp, props, portalNode}},
set: {...s.set, [id]: true}, set: {...s.set, [id]: true},
@ -36,7 +36,7 @@ export const getMounter = () => {
} }
function unmount() { function unmount() {
theAtom.reduceState([], (s) => { theAtom.reduce((s) => {
const set = {...s.set} const set = {...s.set}
const byId = {...s.byId} const byId = {...s.byId}
delete set[id] delete set[id]