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

View file

@ -106,6 +106,8 @@ local function destroy<T>(node: Node<T>)
run_cleanups(node)
unparent(node)
node.effect = false
if node.parents.owner then
remove_child(node.parents.owner, node)
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 apply = require(script.Parent.apply)
local function mount<T>(app: () -> T, target: Instance?): () -> ()
local _, destroy = root(function()
local result = app()
local function mount<T>(component: () -> T, target: Instance?): () -> ()
return root(function(destroy)
local result = component()
if target then apply(target, { result }) end
return nil
end)
return destroy
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 function root<T>(fn: () -> T): (T, () -> ())
local function root<T...>(fn: (destroy: () -> ()) -> T...): T...
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
return result, function()
local destroy = function()
if not refs[node] then throw "root already destroyed" end
refs[node] = nil
destroy(node)
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
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 function WRAP_BENCH(name: string, fn: () -> ())
local _, destroy = vide.root(function()
vide.root(function(destroy)
BENCH(name, fn)
end)
destroy()
return destroy
end)()
end
TITLE "sources"

View file

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