fix: add unmount behavior to split atoms

This commit is contained in:
Richard 2024-03-03 10:24:54 -08:00
parent 4586882d3c
commit fc3cd6d9ad
4 changed files with 25 additions and 16 deletions

View file

@ -125,4 +125,12 @@ return function()
expect(updated).to.equal(1) expect(updated).to.equal(1)
expect(deleted).to.equal(3) expect(deleted).to.equal(3)
end) end)
it("unmounts from parent atom", function()
local itemsAtom = atom({})
local itemAtoms = split(itemsAtom)
expect((next(itemsAtom.listeners))).to.be.ok()
itemAtoms:unmount()
expect((next(itemsAtom.listeners))).to.never.be.ok()
end)
end end

View file

@ -33,16 +33,11 @@ local atomCount = 0
local function atom<State>(state: State): Atom<State> local function atom<State>(state: State): Atom<State>
atomCount += 1 atomCount += 1
local listeners: { [(State) -> ()]: true } = {}
local label = `atom{atomCount}`
local traceback = debug.traceback(nil, 2)
local owners: { [any]: true } = {}
local atom = { local atom = {
listeners = listeners, listeners = {},
debugTraceback = traceback, debugTraceback = debug.traceback(nil, 2),
debugLabel = label, debugLabel = `atom{atomCount}`,
owners = owners, owners = {},
} :: Atom<State> } :: Atom<State>
local equals: ((State, State) -> boolean)? local equals: ((State, State) -> boolean)?
@ -68,7 +63,7 @@ local function atom<State>(state: State): Atom<State>
if not (if equals then equals(state, nextState) else state == nextState) then if not (if equals then equals(state, nextState) else state == nextState) then
state = nextState state = nextState
for listener in table.clone(listeners) do for listener in table.clone(self.listeners) do
listener(state) listener(state)
end end
end end
@ -80,7 +75,7 @@ local function atom<State>(state: State): Atom<State>
if not (if equals then equals(state, nextState) else state == nextState) then if not (if equals then equals(state, nextState) else state == nextState) then
state = nextState state = nextState
for listener in table.clone(listeners) do for listener in table.clone(self.listeners) do
listener(state) listener(state)
end end
end end
@ -99,20 +94,20 @@ local function atom<State>(state: State): Atom<State>
function atom:readonly(): ReadonlyAtom<State> function atom:readonly(): ReadonlyAtom<State>
function setter(_, _) function setter(_, _)
if _G.__DEV__ then if _G.__DEV__ then
error(`Attempt to set read-only atom '{label}' created at: {traceback}`, 3) error(`Attempt to set read-only atom '{self.debugLabel}' created at: {self.debugTraceback}`, 3)
end end
end end
return self return self
end end
function atom:named(name: string): Atom<State> function atom:named(name: string): Atom<State>
label = name self.debugLabel = name
return self return self
end end
function atom:unmount() function atom:unmount()
table.clear(listeners) table.clear(self.listeners)
table.clear(owners) table.clear(self.owners)
reference.release(self) reference.release(self)
end end

View file

@ -29,7 +29,7 @@ local function derive<Result>(...): ReadonlyAtom<Result>
local getDerivedAtom = reference.weak(derivedAtom) local getDerivedAtom = reference.weak(derivedAtom)
function derivedAtom:unmount() function derivedAtom:unmount()
derivedAtomUnmount(self) derivedAtomUnmount(derivedAtom)
for listener in listeners do for listener in listeners do
for _, atom in atoms do for _, atom in atoms do

View file

@ -13,6 +13,7 @@ local function split<Item, Key>(
keyExtractor: ((Item) -> Key)? keyExtractor: ((Item) -> Key)?
): ReadonlyAtom<{ [Key]: Atom<Item> }> ): ReadonlyAtom<{ [Key]: Atom<Item> }>
local splitAtom: ReadonlyAtom<{ [Key]: Atom<Item> }> = atom({}):readonly() local splitAtom: ReadonlyAtom<{ [Key]: Atom<Item> }> = atom({}):readonly()
local splitAtomUnmount = splitAtom.unmount
local getSplitAtom = reference.weak(splitAtom) local getSplitAtom = reference.weak(splitAtom)
local previousAtoms: { [Key]: Atom<Item> } = {} local previousAtoms: { [Key]: Atom<Item> } = {}
@ -67,6 +68,11 @@ local function split<Item, Key>(
splitAtom:setForced(atoms) splitAtom:setForced(atoms)
end end
function splitAtom:unmount()
splitAtomUnmount(splitAtom)
cleanup()
end
itemsAtom.listeners[listener] = true itemsAtom.listeners[listener] = true
listener(itemsAtom:get()) listener(itemsAtom:get())