fix: do not track implicit dependencies in observe() and mapped() (#39)

* fix: use `subscribe` for explicit dependencies

* fix: call listeners initially once

* fix: wrap initial run in `peek()`
This commit is contained in:
richard 2025-01-25 11:51:17 -08:00 • committed by GitHub
parent a430cf63e5
commit 6be2fbb746
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 22 additions and 12 deletions

View file

@ -1,6 +1,6 @@
local atom = require(script.Parent.atom) local atom = require(script.Parent.atom)
local effect = require(script.Parent.effect)
local store = require(script.Parent.store) local store = require(script.Parent.store)
local subscribe = require(script.Parent.subscribe)
local types = require(script.Parent.types) local types = require(script.Parent.types)
type Atom<T> = types.Atom<T> type Atom<T> = types.Atom<T>
type Selector<T> = types.Selector<T> type Selector<T> = types.Selector<T>
@ -26,13 +26,14 @@ local function mapped<K0, V0, K1, V1>(callback: Selector<{ [K0]: V0 }>, mapper:
local prevMappedItems: { [K1]: V1 } = {} local prevMappedItems: { [K1]: V1 } = {}
local unsubscribe local unsubscribe
unsubscribe = effect(function() local function listener(items: { [K0]: V0 })
if not mappedAtomRef.current then local mappedAtom = mappedAtomRef.current
if not mappedAtom then
return unsubscribe() return unsubscribe()
end end
local items = callback() local mappedItems = table.clone(mappedAtom())
local mappedItems = table.clone(store.peek(mappedAtomRef.current))
local mappedKeys = {} local mappedKeys = {}
-- TODO: Only call mapper if the item has changed. -- TODO: Only call mapper if the item has changed.
@ -56,6 +57,12 @@ local function mapped<K0, V0, K1, V1>(callback: Selector<{ [K0]: V0 }>, mapper:
prevMappedItems = mappedItems prevMappedItems = mappedItems
mappedAtom(mappedItems) mappedAtom(mappedItems)
end
unsubscribe = subscribe(callback, listener)
store.peek(function(): ()
listener(callback())
end) end)
return mappedAtom return mappedAtom

View file

@ -1,4 +1,5 @@
local effect = require(script.Parent.effect) local store = require(script.Parent.store)
local subscribe = require(script.Parent.subscribe)
local types = require(script.Parent.types) local types = require(script.Parent.types)
type Selector<T> = types.Selector<T> type Selector<T> = types.Selector<T>
@ -16,9 +17,7 @@ local function noop() end
local function observe<K, V>(callback: Selector<{ [K]: V }>, factory: (value: V, key: K) -> (() -> ())?): () -> () local function observe<K, V>(callback: Selector<{ [K]: V }>, factory: (value: V, key: K) -> (() -> ())?): () -> ()
local connections: { [K]: () -> () } = {} local connections: { [K]: () -> () } = {}
local unsubscribe = effect(function() local function listener(state: { [K]: V })
local state = callback()
for key, disconnect in next, connections do for key, disconnect in next, connections do
if state[key] == nil then if state[key] == nil then
connections[key] = nil connections[key] = nil
@ -31,17 +30,21 @@ local function observe<K, V>(callback: Selector<{ [K]: V }>, factory: (value: V,
connections[key] = factory(value, key) or noop connections[key] = factory(value, key) or noop
end end
end end
end
local unsubscribe = subscribe(callback, listener)
store.peek(function(): ()
listener(callback())
end) end)
local function cleanup() return function()
unsubscribe() unsubscribe()
for _, disconnect in next, connections do for _, disconnect in next, connections do
disconnect() disconnect()
end end
table.clear(connections) table.clear(connections)
end end
return cleanup
end end
return observe return observe