mirror of
https://github.com/littensy/charm.git
synced 2026-10-20 17:51:37 +00:00
fix: wrap iterated values in reactive()
This commit is contained in:
parent
9d4af8e669
commit
73954f56e7
2 changed files with 47 additions and 11 deletions
|
|
@ -81,6 +81,27 @@ test("should track iteration", function()
|
|||
assert(table.concat(calls) == "a=2;b=2;", table.concat(calls))
|
||||
end)
|
||||
|
||||
test("should track iteration for nested tables", function()
|
||||
local src = reactive({} :: { [string]: { value: number } })
|
||||
local calls = {}
|
||||
|
||||
effect(function()
|
||||
for key, obj in src do
|
||||
table.insert(calls, `{key}={obj.value};`)
|
||||
end
|
||||
end)
|
||||
|
||||
src.a = { value = 1 }
|
||||
|
||||
table.clear(calls)
|
||||
src.b = { value = 1 }
|
||||
assert(table.concat(calls) == "a=1;b=1;", table.concat(calls))
|
||||
|
||||
table.clear(calls)
|
||||
src.a.value = 2
|
||||
assert(table.concat(calls) == "a=2;b=1;", table.concat(calls))
|
||||
end)
|
||||
|
||||
test("should track array length", function()
|
||||
local src = reactive({ array = {} :: { number } })
|
||||
local calls = {}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ local function setNotPending(sub: ReactiveNode, flags: ReactiveFlags)
|
|||
end
|
||||
|
||||
local function computedOper<T>(this: ComputedNode<T>): () -> T
|
||||
local function computedGetter(): T
|
||||
local function readComputed(): T
|
||||
local flags = this.flags
|
||||
if
|
||||
bit32.btest(flags, DIRTY)
|
||||
|
|
@ -253,14 +253,14 @@ local function computedOper<T>(this: ComputedNode<T>): () -> T
|
|||
end
|
||||
return this.value :: T
|
||||
end
|
||||
return computedGetter
|
||||
return readComputed
|
||||
end
|
||||
|
||||
local function signalOper<T>(
|
||||
this: SignalNode<T>,
|
||||
equals: ((current: T, incoming: T) -> boolean)?
|
||||
): (() -> T, (((T) -> T) | T) -> T)
|
||||
local function signalSetter(update: ((T) -> T) | T): T
|
||||
local function writeSignal(update: ((T) -> T) | T): T
|
||||
local value: T
|
||||
if type(update) == "function" then
|
||||
local prevSub = setActiveSub(nil)
|
||||
|
|
@ -291,7 +291,7 @@ local function signalOper<T>(
|
|||
return this.currentValue
|
||||
end
|
||||
|
||||
local function signalGetter(): T
|
||||
local function readSignal(): T
|
||||
if bit32.btest(this.flags, DIRTY) then
|
||||
if updateSignal(this) then
|
||||
local subs = this.subs
|
||||
|
|
@ -312,7 +312,7 @@ local function signalOper<T>(
|
|||
return this.currentValue
|
||||
end
|
||||
|
||||
return signalGetter, signalSetter
|
||||
return readSignal, writeSignal
|
||||
end
|
||||
|
||||
local function effectOper(this: EffectScopeNode): () -> ()
|
||||
|
|
@ -406,9 +406,9 @@ end
|
|||
@see https://github.com/littensy/charm?tab=readme-ov-file#atominitialValue-equals
|
||||
]]
|
||||
local function atom<T>(initialValue: T, equals: ((current: T, incoming: T) -> boolean)?): Atom<T>
|
||||
local getter, setter = signal(initialValue, equals)
|
||||
local get, set = signal(initialValue, equals)
|
||||
local function atomOper(...)
|
||||
return if select("#", ...) == 0 then getter() else setter((...))
|
||||
return if select("#", ...) == 0 then get() else set((...))
|
||||
end
|
||||
return atomOper
|
||||
end
|
||||
|
|
@ -625,7 +625,7 @@ end
|
|||
]]
|
||||
local function toSignal<T>(fn: () -> T): () -> T
|
||||
local name = debug.info(fn, "n")
|
||||
return if name == "signalGetter" or name == "computedGetter" or name == "atomOper" then fn else computed(fn)
|
||||
return if name == "readSignal" or name == "readComputed" or name == "atomOper" then fn else computed(fn)
|
||||
end
|
||||
|
||||
--[[
|
||||
|
|
@ -922,11 +922,22 @@ local function reactive<T>(initialValue: T & {}, trackParent: (() -> ())?): (T,
|
|||
return proxy :: any, setValue
|
||||
end
|
||||
|
||||
--[[
|
||||
Wraps a value in `reactive()` if it is a table; otherwise, returns the
|
||||
value as-is.
|
||||
|
||||
@param value A value to make reactive.
|
||||
@param trackParent An optional tracking function to propagate deep updates.
|
||||
@return A reactive proxy if the value is a table.
|
||||
]]
|
||||
local function toReactive<T>(value: T, trackParent: (() -> ())?): T
|
||||
return if type(value) == "table" then reactive(value, trackParent) else value
|
||||
end
|
||||
|
||||
function ReactiveProxy.__index(self: ReactiveProxy, key: any): any
|
||||
local track = self.__track
|
||||
local signals = self.__signals
|
||||
local target = self.__target
|
||||
local value = target[key]
|
||||
|
||||
if activeSub then
|
||||
if not signals[key] then
|
||||
|
|
@ -938,7 +949,7 @@ function ReactiveProxy.__index(self: ReactiveProxy, key: any): any
|
|||
signals[key]()
|
||||
end
|
||||
|
||||
return if type(value) == "table" then reactive(value, track) else value
|
||||
return toReactive(target[key], track)
|
||||
end
|
||||
|
||||
function ReactiveProxy.__newindex(self: ReactiveProxy, key: any, value: any)
|
||||
|
|
@ -951,7 +962,11 @@ end
|
|||
|
||||
function ReactiveProxy.__iter(self: ReactiveProxy)
|
||||
self.__track()
|
||||
return next, self.__target
|
||||
local function nextReactive(target: any, prevKey: any)
|
||||
local nextKey, nextValue = next(target, prevKey)
|
||||
return nextKey, toReactive(nextValue, self.__track)
|
||||
end
|
||||
return nextReactive, self.__target
|
||||
end
|
||||
|
||||
function ReactiveProxy.__len(self: ReactiveProxy)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue