mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
This commit is contained in:
parent
5a458eae71
commit
68ff117be9
19 changed files with 80 additions and 83 deletions
|
|
@ -50,16 +50,16 @@ Creates a new source with the given value.
|
|||
count(count() + 1) -- 1
|
||||
```
|
||||
|
||||
## watch()
|
||||
## effect()
|
||||
|
||||
Runs a callback on source update.
|
||||
|
||||
- **Type**
|
||||
|
||||
```lua
|
||||
function watch(source: () -> ()): Unwatch
|
||||
function effect(source: () -> ()): Uneffect
|
||||
|
||||
type Unwatch = () -> ()
|
||||
type Uneffect = () -> ()
|
||||
```
|
||||
|
||||
- **Details**
|
||||
|
|
@ -70,7 +70,7 @@ Runs a callback on source update.
|
|||
Any time a source referenced in the callback is changed, the callback will
|
||||
be reran.
|
||||
|
||||
Also returns a function that when called, stops the watcher immediately.
|
||||
Also returns a function that when called, stops the effecter immediately.
|
||||
|
||||
::: warning
|
||||
`source()` cannot yield.
|
||||
|
|
@ -81,7 +81,7 @@ Runs a callback on source update.
|
|||
```lua
|
||||
local state = source(1)
|
||||
|
||||
watch(function()
|
||||
effect(function()
|
||||
print(state())
|
||||
end)
|
||||
|
||||
|
|
|
|||
0
docs/api/reactivity-flow.md
Normal file
0
docs/api/reactivity-flow.md
Normal file
|
|
@ -15,7 +15,7 @@ Runs a callback anytime a reactive scope is re-ran.
|
|||
```lua
|
||||
local data = source(1)
|
||||
|
||||
watch(function()
|
||||
effect(function()
|
||||
local label = create "TextLabel" { Text = data() }
|
||||
|
||||
cleanup(function()
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ and identifying improper usage.
|
|||
Currently, strict mode will:
|
||||
|
||||
1. Run derived sources twice a source updates.
|
||||
2. Run watchers twice when a source updates.
|
||||
2. Run effecters twice when a source updates.
|
||||
3. Throw an error if yields occur where they are not allowed.
|
||||
4. Checks for `indexes()` and `values()` returning primitive values.
|
||||
5. Checks for duplicate nested properties at same depth.
|
||||
6. Better error reporting and stack traces.
|
||||
7. Checks for multiple `cleanup()` calls in the same function scope.
|
||||
|
||||
By rerunning sources and watchers, any side-effects are made more apparent.
|
||||
By rerunning sources and effecters, any side-effects are made more apparent.
|
||||
This also helps ensure that cleanups are being handled correctly.
|
||||
|
||||
Accidental yielding within reactive scopes can break Vide's reactive graph,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Instances are created using [`create()`](../../api/creation.md#create).
|
||||
|
||||
```lua
|
||||
local vide = require(path_to_vide)
|
||||
local vide = require(vide)
|
||||
local create = vide.create
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ Components are custom-made reusable pieces of UI made from other pieces of UI.
|
|||
By using components you can make your application more modular and better
|
||||
organized.
|
||||
|
||||
```lua [Button.lua]
|
||||
```lua [Button.luau]
|
||||
local vide = require(vide)
|
||||
local create = vide.create
|
||||
|
||||
local function Button(props: {
|
||||
Position: UDim2,
|
||||
Text: string,
|
||||
|
|
@ -24,7 +27,10 @@ end
|
|||
return Button
|
||||
```
|
||||
|
||||
```lua [App.lua]
|
||||
```lua [App.luau]
|
||||
local vide = require(vide)
|
||||
local create = vide.create
|
||||
|
||||
local Button = require(Button)
|
||||
|
||||
local function App()
|
||||
|
|
@ -34,7 +40,7 @@ local function App()
|
|||
Text = "click me!",
|
||||
|
||||
Activated = function()
|
||||
print "clickeD"
|
||||
print "clicked"
|
||||
end
|
||||
}
|
||||
}
|
||||
|
|
@ -43,27 +49,17 @@ end
|
|||
root(App).Parent = game.StarterGui
|
||||
```
|
||||
|
||||
Above is a simple example of a button component with its background color set to
|
||||
a dark grey and with a fixed size.
|
||||
Above is a simple example of a button component with a set color and size,
|
||||
being reused across files.
|
||||
|
||||
A single parameter `props` is used to pass properties to the component.
|
||||
Creating instances of this button component is as simple as doing the below:
|
||||
|
||||
```lua
|
||||
local button = Button {
|
||||
Position = UDim2.new(),
|
||||
Text = "Click me!",
|
||||
|
||||
Activated = function()
|
||||
print "clicked"
|
||||
end
|
||||
}
|
||||
```
|
||||
|
||||
Components allow you to *encapsulate* behavior. You can only modify the
|
||||
component in ways that you allow in the component.
|
||||
|
||||
This also promotes code reusability. Anytime you want a new button all you do
|
||||
is call `Button {}` instead of creating and setting every property each time.
|
||||
When changing the button in future, any changes to the button file will be
|
||||
reflected anywhere the button is used throughout your app.
|
||||
|
||||
This can be extended to much more complicated UI.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ A source in Vide can be created using
|
|||
|
||||
```lua
|
||||
local source = vide.source
|
||||
|
||||
local count = source(0)
|
||||
```
|
||||
|
||||
|
|
@ -24,7 +23,10 @@ count(count() + 1) -- increment source by 1
|
|||
|
||||
Below is an example of a stateful counter component.
|
||||
|
||||
```lua
|
||||
```lua [Counter.luau]
|
||||
local vide = require(vide)
|
||||
local source = vide.source
|
||||
|
||||
local function Counter(props: { Position: UDim2 })
|
||||
local count = source(0)
|
||||
|
||||
|
|
@ -54,22 +56,3 @@ This allows you as the programmer to not need to manually update GUI as the stat
|
|||
of your program changes. You just define how the data maps to UI, and Vide's
|
||||
reactive system will surgically update any properties depending on sources that
|
||||
are changed.
|
||||
|
||||
Since sources are just functions, you can also pass external sources to
|
||||
components like so:
|
||||
|
||||
```lua
|
||||
local function Text(p: {
|
||||
Text: () -> string
|
||||
})
|
||||
return create "TextLabel" {
|
||||
Text = p.Text
|
||||
}
|
||||
end
|
||||
|
||||
local text = source "hi"
|
||||
|
||||
Text {
|
||||
Text = text
|
||||
}
|
||||
```
|
||||
|
|
|
|||
0
docs/tut/crash-course/5-effect.md
Normal file
0
docs/tut/crash-course/5-effect.md
Normal file
|
|
@ -63,7 +63,7 @@ callback when deriving or binding sources. If a source is in a function but is
|
|||
never referenced the first time it runs, Vide will not know to rerun the
|
||||
function if that source changes.
|
||||
|
||||
An example to watch out for is when using sources within branches:
|
||||
An example to effect out for is when using sources within branches:
|
||||
|
||||
```lua
|
||||
local condition = source(true)
|
||||
0
docs/tut/crash-course/8-cleanup.md
Normal file
0
docs/tut/crash-course/8-cleanup.md
Normal file
18
src/changed.luau
Normal file
18
src/changed.luau
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
if not game then script = require "test/relative-string" end
|
||||
|
||||
local action = require(script.Parent.action)()
|
||||
local cleanup = require(script.Parent.cleanup)
|
||||
|
||||
local function changed<T>(property: string, callback: (T) -> ())
|
||||
return action(function(instance)
|
||||
local con = instance:GetPropertyChangedSignal(property):Connect(function()
|
||||
callback((instance :: any)[property])
|
||||
end)
|
||||
|
||||
cleanup(function()
|
||||
con:Disconnect()
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
return changed
|
||||
|
|
@ -8,9 +8,9 @@ local open_scope = graph.open_scope
|
|||
local close_scope = graph.close_scope
|
||||
local set_owner = graph.set_owner
|
||||
|
||||
local function watch<T>(effect: (T) -> T, initial_value: T)
|
||||
local function effect<T>(effect: (T) -> T, initial_value: T)
|
||||
local owner = get_scope()
|
||||
if not owner then throw("cannot watch in non-reactive scope") end
|
||||
if not owner then throw("cannot effect in non-reactive scope") end
|
||||
assert(owner)
|
||||
|
||||
local node = create_node(initial_value)
|
||||
|
|
@ -24,4 +24,4 @@ local function watch<T>(effect: (T) -> T, initial_value: T)
|
|||
close_scope()
|
||||
end
|
||||
|
||||
return watch :: (<T>(effect: (T) -> T, initial_value: T) -> ()) & ((effect: () -> ()) -> ())
|
||||
return effect :: (<T>(effect: (T) -> T, initial_value: T) -> ()) & ((effect: () -> ()) -> ())
|
||||
|
|
@ -41,7 +41,7 @@ local check_for_yield: <T...>(fn: (T...) -> unknown, T...) -> () do
|
|||
|
||||
if not ok then
|
||||
if err == "attempt to yield across metamethod/C-call boundary" or err == "thread is not yieldable" then
|
||||
throw(EVALUATION_ERR .. "cannot yield when deriving node in watcher")
|
||||
throw(EVALUATION_ERR .. "cannot yield when deriving node in effecter")
|
||||
else
|
||||
throw(EVALUATION_ERR .. err)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ local root = require(script.root)
|
|||
local create = require(script.create)
|
||||
local apply = require(script.apply)
|
||||
local source = require(script.source)
|
||||
local watch = require(script.watch)
|
||||
local effect = require(script.effect)
|
||||
local cleanup = require(script.cleanup)
|
||||
local untrack = require(script.untrack)
|
||||
local derive = require(script.derive)
|
||||
|
|
@ -52,7 +52,7 @@ local vide = {
|
|||
root = root,
|
||||
create = create,
|
||||
source = source,
|
||||
watch = watch,
|
||||
effect = effect,
|
||||
derive = derive,
|
||||
indexes = indexes,
|
||||
values = values,
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ local function main()
|
|||
|
||||
local source = vide.source
|
||||
local spring = vide.spring
|
||||
local watch = vide.watch
|
||||
local effect = vide.effect
|
||||
|
||||
local value = source(MAX)
|
||||
local sprung = spring(value, 1, 0.3)
|
||||
|
||||
watch(function()
|
||||
effect(function()
|
||||
local v = sprung()
|
||||
local fv = math.floor(v)
|
||||
local reset = "\27[H\27[2J" -- ANSI clear terminal
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ end)
|
|||
|
||||
TEST("source()", wrap_root(function()
|
||||
local source = vide.source
|
||||
local watch = vide.watch
|
||||
local effect = vide.effect
|
||||
|
||||
do CASE "create source"
|
||||
local src = source(1)
|
||||
|
|
@ -278,7 +278,7 @@ TEST("source()", wrap_root(function()
|
|||
local src = source(1)
|
||||
|
||||
local count = 0
|
||||
watch(function()
|
||||
effect(function()
|
||||
src()
|
||||
count += 1
|
||||
end)
|
||||
|
|
@ -294,7 +294,7 @@ TEST("source()", wrap_root(function()
|
|||
local src = source {}
|
||||
|
||||
local count = 0
|
||||
watch(function()
|
||||
effect(function()
|
||||
src()
|
||||
count += 1
|
||||
end)
|
||||
|
|
@ -311,7 +311,7 @@ TEST("source()", wrap_root(function()
|
|||
local src = source(a)
|
||||
|
||||
local count = 0
|
||||
watch(function()
|
||||
effect(function()
|
||||
src()
|
||||
count += 1
|
||||
end)
|
||||
|
|
@ -330,7 +330,7 @@ TEST("derive()", wrap_root(function()
|
|||
local root = vide.root
|
||||
local source = vide.source
|
||||
local derive = vide.derive
|
||||
local watch = vide.watch
|
||||
local effect = vide.effect
|
||||
local cleanup = vide.cleanup
|
||||
|
||||
do CASE "derive new value on source change"
|
||||
|
|
@ -371,7 +371,7 @@ TEST("derive()", wrap_root(function()
|
|||
|
||||
local count = 0
|
||||
|
||||
watch(function()
|
||||
effect(function()
|
||||
is_even()
|
||||
count += 1
|
||||
end)
|
||||
|
|
@ -399,7 +399,7 @@ TEST("derive()", wrap_root(function()
|
|||
|
||||
local count = 0
|
||||
|
||||
watch(function() c() count += 1 end)
|
||||
effect(function() c() count += 1 end)
|
||||
|
||||
b(true)
|
||||
CHECK(c() == "b")
|
||||
|
|
@ -455,16 +455,16 @@ TEST("derive()", wrap_root(function()
|
|||
end
|
||||
end))
|
||||
|
||||
TEST("watch()", wrap_root(function()
|
||||
TEST("effect()", wrap_root(function()
|
||||
local source = vide.source
|
||||
local watch = vide.watch
|
||||
local effect = vide.effect
|
||||
|
||||
do CASE "rerun on source change"
|
||||
local a = source(1)
|
||||
local b = source(1)
|
||||
|
||||
local count = 0
|
||||
watch(function()
|
||||
effect(function()
|
||||
a()
|
||||
b()
|
||||
count += 1
|
||||
|
|
@ -481,7 +481,7 @@ end))
|
|||
TEST("cleanup()", wrap_root(function()
|
||||
local root = vide.root
|
||||
local source = vide.source
|
||||
local watch = vide.watch
|
||||
local effect = vide.effect
|
||||
local cleanup = vide.cleanup
|
||||
|
||||
do CASE "root cleanup"
|
||||
|
|
@ -503,23 +503,23 @@ TEST("cleanup()", wrap_root(function()
|
|||
do CASE "cleanup on rerun"
|
||||
local state = source(1)
|
||||
|
||||
local watched = 0
|
||||
local effected = 0
|
||||
local cleaned = 0
|
||||
|
||||
watch(function()
|
||||
effect(function()
|
||||
state()
|
||||
watched += 1
|
||||
effected += 1
|
||||
cleanup(function()
|
||||
cleaned += 1
|
||||
end)
|
||||
end)
|
||||
|
||||
CHECK(watched == 1)
|
||||
CHECK(effected == 1)
|
||||
CHECK(cleaned == 0)
|
||||
|
||||
state(2)
|
||||
|
||||
CHECK(watched == 2)
|
||||
CHECK(effected == 2)
|
||||
CHECK(cleaned == 1)
|
||||
end
|
||||
|
||||
|
|
@ -528,7 +528,7 @@ TEST("cleanup()", wrap_root(function()
|
|||
|
||||
local queue = {}
|
||||
|
||||
watch(function()
|
||||
effect(function()
|
||||
state()
|
||||
cleanup(function() table.insert(queue, 1) end)
|
||||
cleanup(function() table.insert(queue, 2) end)
|
||||
|
|
@ -1001,7 +1001,7 @@ TEST("spring()", wrap_root(function()
|
|||
local create = vide.create
|
||||
local source = vide.source
|
||||
local spring = vide.spring
|
||||
local watch = vide.watch
|
||||
local effect = vide.effect
|
||||
|
||||
do CASE "update source (on next step)"
|
||||
local value = source(10)
|
||||
|
|
@ -1082,7 +1082,7 @@ TEST("spring()", wrap_root(function()
|
|||
CHECK(output() == input()) -- check spring is at target
|
||||
|
||||
local count = -1
|
||||
watch(function()
|
||||
effect(function()
|
||||
output()
|
||||
count += 1
|
||||
end)
|
||||
|
|
@ -1102,7 +1102,7 @@ TEST("untrack()", wrap_root(function()
|
|||
local root = vide.root
|
||||
local source = vide.source
|
||||
local derive = vide.derive
|
||||
local watch = vide.watch
|
||||
local effect = vide.effect
|
||||
local cleanup = vide.cleanup
|
||||
local untrack = vide.untrack
|
||||
|
||||
|
|
@ -1112,7 +1112,7 @@ TEST("untrack()", wrap_root(function()
|
|||
|
||||
local count = 0
|
||||
|
||||
watch(function()
|
||||
effect(function()
|
||||
count += 1
|
||||
untrack(a)
|
||||
b()
|
||||
|
|
@ -1138,7 +1138,7 @@ TEST("untrack()", wrap_root(function()
|
|||
|
||||
local count = 0
|
||||
|
||||
watch(function()
|
||||
effect(function()
|
||||
count += 1
|
||||
untrack(d)
|
||||
c()
|
||||
|
|
@ -1275,7 +1275,7 @@ end)
|
|||
-- local create = vide.create
|
||||
-- local source = vide.source
|
||||
-- local derive = vide.derive
|
||||
-- local watch = vide.watch
|
||||
-- local effect = vide.effect
|
||||
-- local indexes, values = vide.indexes, vide.values
|
||||
-- local cleanup = vide.cleanup
|
||||
|
||||
|
|
@ -1292,11 +1292,11 @@ end)
|
|||
-- -- CHECK(not ok)
|
||||
-- -- end
|
||||
|
||||
-- -- do CASE "error on watcher callback yield"
|
||||
-- -- do CASE "error on effecter callback yield"
|
||||
-- -- local state = source(1)
|
||||
|
||||
-- -- local ok = pcall(function()
|
||||
-- -- local _derived = watch(function()
|
||||
-- -- local _derived = effect(function()
|
||||
-- -- coroutine.yield()
|
||||
-- -- state()
|
||||
-- -- end)
|
||||
|
|
@ -1319,11 +1319,11 @@ end)
|
|||
-- CHECK(runcount == 4)
|
||||
-- end
|
||||
|
||||
-- do CASE "run watcher callback twice"
|
||||
-- do CASE "run effecter callback twice"
|
||||
-- local state = source(1)
|
||||
-- local runcount = 0
|
||||
|
||||
-- watch(function()
|
||||
-- effect(function()
|
||||
-- runcount += 1
|
||||
-- state()
|
||||
-- end)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue