This commit is contained in:
aaron 2023-09-15 12:51:39 +01:00
parent bbe634f433
commit 31b1956b5a
6 changed files with 54 additions and 41 deletions

View file

@ -9,7 +9,7 @@ Creates and runs a function in a new reactive scope.
- **Type** - **Type**
```lua ```lua
function root<T>(fn: () -> T): (T, () -> ()) function root<T...>(fn: (destroy: () -> ()) -> T...): T...
``` ```
- **Details** - **Details**
@ -19,8 +19,8 @@ Creates and runs a function in a new reactive scope.
Returns the result of the given function. Returns the result of the given function.
Also returns a function to destroy the root, which will run any cleanups A function to destroy the root is passed into the callback, which will run
and allow derived sources created to garbage collect. any cleanups and allow derived sources created to garbage collect.
::: warning ::: warning
`fn()` cannot yield. `fn()` cannot yield.

View file

@ -106,6 +106,8 @@ local function destroy<T>(node: Node<T>)
run_cleanups(node) run_cleanups(node)
unparent(node) unparent(node)
node.effect = false
if node.parents.owner then if node.parents.owner then
remove_child(node.parents.owner, node) remove_child(node.parents.owner, node)
node.parents.owner = nil node.parents.owner = nil

View file

@ -3,14 +3,12 @@ if not game then script = require "test/relative-string" end
local root = require(script.Parent.root) local root = require(script.Parent.root)
local apply = require(script.Parent.apply) local apply = require(script.Parent.apply)
local function mount<T>(app: () -> T, target: Instance?): () -> () local function mount<T>(component: () -> T, target: Instance?): () -> ()
local _, destroy = root(function() return root(function(destroy)
local result = app() local result = component()
if target then apply(target, { result }) end if target then apply(target, { result }) end
return nil
end)
return destroy return destroy
end)
end end
return mount return mount :: (<T>(component: () -> T, target: Instance) -> () -> ()) & ((component: () -> ()) -> () -> ())

View file

@ -10,25 +10,29 @@ local destroy = graph.destroy
local refs = {} local refs = {}
local function root<T>(fn: () -> T): (T, () -> ()) local function root<T...>(fn: (destroy: () -> ()) -> T...): T...
local node = create_node(false, false) local node = create_node(false, false)
open_scope(node)
local ok, result = pcall(fn)
close_scope()
if not ok then
throw(`mount error\n{result}`)
end
refs[node] = true -- prevent gc of root node refs[node] = true -- prevent gc of root node
return result, function() local destroy = function()
if not refs[node] then throw "root already destroyed" end
refs[node] = nil refs[node] = nil
destroy(node) destroy(node)
end end
open_scope(node)
local result = { pcall(fn, destroy) }
close_scope()
if not result[1] then
refs[node] = nil
throw(`mount error\n{result}`)
end
return unpack(result :: any, 2)
end end
return root :: (<T>(fn: () -> T) -> (T, () -> ())) & ((fn: () -> ()) -> (nil, () -> ())) return root :: (<T...>(fn: (destroy: () -> ()) -> T...) -> T...) & ((fn: (destroy: () -> ()) -> ()) -> ())

View file

@ -17,10 +17,10 @@ end
local N = 2^18 -- 262144 local N = 2^18 -- 262144
local function WRAP_BENCH(name: string, fn: () -> ()) local function WRAP_BENCH(name: string, fn: () -> ())
local _, destroy = vide.root(function() vide.root(function(destroy)
BENCH(name, fn) BENCH(name, fn)
end) return destroy
destroy() end)()
end end
TITLE "sources" TITLE "sources"

View file

@ -24,7 +24,7 @@ end
local function wrap_root(fn: () -> ()) local function wrap_root(fn: () -> ())
return function() return function()
local _, destroy = vide.root(fn :: any) local destroy = vide.mount(fn :: any)
destroy() destroy()
end end
end end
@ -289,6 +289,20 @@ TEST("mount()", function()
CHECK(count == 1) CHECK(count == 1)
end) end)
TEST("root()", function()
local root = vide.root
local cleanup = vide.cleanup
local count = 0
root(function(destroy)
cleanup(function() count += 1 end)
destroy()
end)
CHECK(count == 1)
end)
TEST("source()", wrap_root(function() TEST("source()", wrap_root(function()
local source = vide.source local source = vide.source
local effect = vide.effect local effect = vide.effect
@ -357,7 +371,6 @@ TEST("source()", wrap_root(function()
end)) end))
TEST("derive()", wrap_root(function() TEST("derive()", wrap_root(function()
local root = vide.root
local source = vide.source local source = vide.source
local derive = vide.derive local derive = vide.derive
local effect = vide.effect local effect = vide.effect
@ -450,7 +463,7 @@ TEST("derive()", wrap_root(function()
local count = 0 local count = 0
local a = source(0) local a = source(0)
local _, destroy = root(function() local destroy = vide.mount(function()
local _b = derive(function() local _b = derive(function()
cleanup(function() cleanup(function()
@ -542,7 +555,6 @@ TEST("effect()", wrap_root(function()
end)) end))
TEST("cleanup()", wrap_root(function() TEST("cleanup()", wrap_root(function()
local root = vide.root
local source = vide.source local source = vide.source
local effect = vide.effect local effect = vide.effect
local cleanup = vide.cleanup local cleanup = vide.cleanup
@ -550,12 +562,10 @@ TEST("cleanup()", wrap_root(function()
do CASE "root cleanup" do CASE "root cleanup"
local count = 0 local count = 0
local _, destroy = root(function() local destroy = vide.mount(function()
cleanup(function() cleanup(function()
count += 1 count += 1
end) end)
return nil
end) end)
CHECK(count == 0) CHECK(count == 0)
@ -606,7 +616,6 @@ TEST("cleanup()", wrap_root(function()
end)) end))
TEST("create()", wrap_root(function() TEST("create()", wrap_root(function()
local root = vide.root
local create = vide.create local create = vide.create
local source = vide.source local source = vide.source
local cleanup = vide.cleanup local cleanup = vide.cleanup
@ -716,7 +725,7 @@ TEST("create()", wrap_root(function()
do CASE "binding destroy" do CASE "binding destroy"
local count = 0 local count = 0
local _, destroy = root(function() local destroy = vide.mount(function()
local src = source(0) local src = source(0)
return create "TextLabel" { return create "TextLabel" {
@ -782,7 +791,7 @@ TEST("create()", wrap_root(function()
end end
do CASE "parent bound to source" do CASE "parent bound to source"
local wref, destroy = root(function() local wref, destroy = vide.root(function(destroy)
local frame = create "Frame" { Name = "Parent" } local frame = create "Frame" { Name = "Parent" }
local parent = source(frame :: Frame?) local parent = source(frame :: Frame?)
@ -795,7 +804,7 @@ TEST("create()", wrap_root(function()
parent(nil) parent(nil)
return wref return wref, destroy
end) end)
gc() gc()
@ -1373,7 +1382,7 @@ TEST("untrack()", wrap_root(function()
local input = source(0) local input = source(0)
local output, destroy = root(function() local output, destroy = root(function(destroy)
local output = derive(function() local output = derive(function()
outer_count += 1 outer_count += 1
@ -1390,7 +1399,7 @@ TEST("untrack()", wrap_root(function()
end) end)
end) end)
return output return output, destroy
end) end)
CHECK(outer_count == 1) CHECK(outer_count == 1)
@ -1503,13 +1512,13 @@ TEST("changed()", wrap_root(function()
end end
do CASE "connection disconnected" do CASE "connection disconnected"
local text, destroy = root(function() local text, destroy = root(function(destroy)
local output = source(nil) local output = source(nil)
return create "TextLabel" { return create "TextLabel" {
Text = "a", Text = "a",
changed("Text", output) changed("Text", output)
} }, destroy
end) end)
destroy() -- changed() should of disconnect connection destroy() -- changed() should of disconnect connection