diff --git a/src/show.luau b/src/show.luau index e33393f..c4d87db 100644 --- a/src/show.luau +++ b/src/show.luau @@ -1,28 +1,33 @@ +local source = require "./source" local derive = require "./derive" +local effect = require "./effect" local untrack = require "./untrack" -local function show(source: () -> T?, component: (() -> T) -> U, fallback: (() -> U)?): () -> U? - local truthy = derive(function() - return not not source() +local function show(input: () -> T?, component: (() -> T) -> U, fallback: (() -> U)?): () -> U? + local filtered_input = source() + + effect(function() + local v = input() + if v then + filtered_input(v) + end end) - -- seemingly redundant derivation to extend the reactive graph so that - -- the propogation of the source's update is delayed, giving time for - -- 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() + local input_is_truthy = derive(function() + return not not input() end) + -- todo: is this needed? + -- local filtered_input_is_truthy = derive(function() + -- return not not filtered_input() + -- end) + return derive(function() 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) else nil end) end -return show :: ( - ( (source: () -> T?, component: (() -> T) -> U) -> () -> U? ) -) +return show diff --git a/test/tests.luau b/test/tests.luau index 36a22b8..cd627fc 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -1153,6 +1153,80 @@ TEST("show()", wrap_root(function() CHECK(count == 3) 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 local visible = vide.source(true) local count = vide.source(0)