From 6be2fbb746c14d4a76916e7f5e6e14dd2348d0bc Mon Sep 17 00:00:00 2001 From: richard <56808540+littensy@users.noreply.github.com> Date: Sat, 25 Jan 2025 11:51:17 -0800 Subject: [PATCH] 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()` --- src/charm/src/mapped.luau | 17 ++++++++++++----- src/charm/src/observe.luau | 17 ++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/charm/src/mapped.luau b/src/charm/src/mapped.luau index 1f1e977..555aa3f 100644 --- a/src/charm/src/mapped.luau +++ b/src/charm/src/mapped.luau @@ -1,6 +1,6 @@ local atom = require(script.Parent.atom) -local effect = require(script.Parent.effect) local store = require(script.Parent.store) +local subscribe = require(script.Parent.subscribe) local types = require(script.Parent.types) type Atom = types.Atom type Selector = types.Selector @@ -26,13 +26,14 @@ local function mapped(callback: Selector<{ [K0]: V0 }>, mapper: local prevMappedItems: { [K1]: V1 } = {} local unsubscribe - unsubscribe = effect(function() - if not mappedAtomRef.current then + local function listener(items: { [K0]: V0 }) + local mappedAtom = mappedAtomRef.current + + if not mappedAtom then return unsubscribe() end - local items = callback() - local mappedItems = table.clone(store.peek(mappedAtomRef.current)) + local mappedItems = table.clone(mappedAtom()) local mappedKeys = {} -- TODO: Only call mapper if the item has changed. @@ -56,6 +57,12 @@ local function mapped(callback: Selector<{ [K0]: V0 }>, mapper: prevMappedItems = mappedItems mappedAtom(mappedItems) + end + + unsubscribe = subscribe(callback, listener) + + store.peek(function(): () + listener(callback()) end) return mappedAtom diff --git a/src/charm/src/observe.luau b/src/charm/src/observe.luau index 5bb0296..120ae3b 100644 --- a/src/charm/src/observe.luau +++ b/src/charm/src/observe.luau @@ -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) type Selector = types.Selector @@ -16,9 +17,7 @@ local function noop() end local function observe(callback: Selector<{ [K]: V }>, factory: (value: V, key: K) -> (() -> ())?): () -> () local connections: { [K]: () -> () } = {} - local unsubscribe = effect(function() - local state = callback() - + local function listener(state: { [K]: V }) for key, disconnect in next, connections do if state[key] == nil then connections[key] = nil @@ -31,17 +30,21 @@ local function observe(callback: Selector<{ [K]: V }>, factory: (value: V, connections[key] = factory(value, key) or noop end end + end + + local unsubscribe = subscribe(callback, listener) + + store.peek(function(): () + listener(callback()) end) - local function cleanup() + return function() unsubscribe() for _, disconnect in next, connections do disconnect() end table.clear(connections) end - - return cleanup end return observe