Fix show() edge case

This commit is contained in:
aaron 2025-01-06 23:56:49 +00:00
parent 46a2043356
commit a383c4ce69
2 changed files with 93 additions and 14 deletions

View file

@ -1,28 +1,33 @@
local source = require "./source"
local derive = require "./derive" local derive = require "./derive"
local effect = require "./effect"
local untrack = require "./untrack" local untrack = require "./untrack"
local function show<T, U>(source: () -> T?, component: (() -> T) -> U, fallback: (() -> U)?): () -> U? local function show<T, U>(input: () -> T?, component: (() -> T) -> U, fallback: (() -> U)?): () -> U?
local truthy = derive(function() local filtered_input = source()
return not not source()
effect(function()
local v = input()
if v then
filtered_input(v)
end
end) end)
-- seemingly redundant derivation to extend the reactive graph so that local input_is_truthy = derive(function()
-- the propogation of the source's update is delayed, giving time for return not not input()
-- the show() scope to be destroyed before potential effects registered
-- by the component can run when they should not run
-- todo: are there cases this method does not cover?
local derived = derive(function()
return source()
end) end)
-- todo: is this needed?
-- local filtered_input_is_truthy = derive(function()
-- return not not filtered_input()
-- end)
return derive(function() return derive(function()
return return
if truthy() then untrack(function() return component(derived :: () -> T) end) if input_is_truthy() then untrack(function() return component(filtered_input :: () -> T) end)
elseif fallback then untrack(fallback) elseif fallback then untrack(fallback)
else nil else nil
end) end)
end end
return show :: ( return show
( <T, U>(source: () -> T?, component: (() -> T) -> U) -> () -> U? )
)

View file

@ -1153,6 +1153,80 @@ TEST("show()", wrap_root(function()
CHECK(count == 3) CHECK(count == 3)
end end
do CASE "special strict case"
type Weapon = {
id: string,
enchant: string?
}
vide.strict = true
local count = 0
local branch = 0
local weapon = source(nil :: Weapon?)
effect(function()
weapon()
end)
effect(function()
weapon()
end)
show(weapon, function(weapon: () -> Weapon)
local enchant = function() return weapon().enchant end
show(enchant, function(enchant: () -> string)
effect(function()
local e = enchant()
count += 1
CHECK(e ~= nil)
if branch == 1 then
CHECK(e == "fire")
elseif branch == 2 then
CHECK(e == "poison")
end
end)
return {}
end)
return {}
end)
effect(function()
weapon()
end)
effect(function()
weapon()
end)
branch = 1
weapon { id = "1", enchant = "fire" }
CHECK(count == 8)
branch = 2
weapon { id = "1", enchant = "poison" }
CHECK(count == 10)
weapon { id = "1", enchant = nil }
CHECK(count == 10)
branch = 1
weapon { id = "1", enchant = "fire" }
CHECK(count == 14)
weapon(nil)
branch = 2
weapon { id = "1", enchant = "poison" }
CHECK(count == 22)
vide.strict = false
end
do CASE "alt" -- todo: move test do CASE "alt" -- todo: move test
local visible = vide.source(true) local visible = vide.source(true)
local count = vide.source(0) local count = vide.source(0)