mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Update docs
This commit is contained in:
parent
f2de9b0e63
commit
62f1c3a20a
16 changed files with 86 additions and 347 deletions
|
|
@ -49,10 +49,9 @@ export default withMermaid({
|
||||||
{ text: "Derived Sources", link: "/tut/crash-course/9-derived-source" },
|
{ text: "Derived Sources", link: "/tut/crash-course/9-derived-source" },
|
||||||
{ text: "Cleanup", link: "/tut/crash-course/10-cleanup" },
|
{ text: "Cleanup", link: "/tut/crash-course/10-cleanup" },
|
||||||
{ text: "Control Flow", link: "/tut/crash-course/11-control-flow" },
|
{ text: "Control Flow", link: "/tut/crash-course/11-control-flow" },
|
||||||
{ text: "Property Nesting", link: "/tut/crash-course/12-property-nesting" },
|
{ text: "Actions", link: "/tut/crash-course/12-actions" },
|
||||||
{ text: "Actions", link: "/tut/crash-course/13-actions" },
|
{ text: "Strict Mode", link: "/tut/crash-course/13-strict-mode" },
|
||||||
{ text: "Strict Mode", link: "/tut/crash-course/14-strict-mode" },
|
{ text: "Concepts Summary", link: "/tut/crash-course/14-concepts" }
|
||||||
{ text: "Concepts Summary", link: "/tut/crash-course/15-concepts" }
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,12 @@ This is a tutorial that introduces the concepts and usage of Vide.
|
||||||
|
|
||||||
Vide is heavily inspired by [Solid](https://www.solidjs.com/).
|
Vide is heavily inspired by [Solid](https://www.solidjs.com/).
|
||||||
|
|
||||||
This tutorial assumes familiarity with Luau and Roblox UI.
|
|
||||||
|
|
||||||
## Why Vide?
|
## Why Vide?
|
||||||
|
|
||||||
Creating UI is complicated, slow, and tedious.
|
Vide provides a reactive and declarative API to simplify managing UI.
|
||||||
|
|
||||||
Vide tries to simplify and speed up this process by providing a declarative and
|
|
||||||
reactive of style programming, which lets you focus more on designing the UI
|
|
||||||
itself and not having to manually update or reparent UI instances.
|
|
||||||
|
|
||||||
Some of the main focuses behind Vide's design choices:
|
Some of the main focuses behind Vide's design choices:
|
||||||
|
|
||||||
- Minimal syntax.
|
- Minimal syntax
|
||||||
- Complete typechecking
|
- Complete typechecking
|
||||||
- Independence from instances.
|
- Independence from instances
|
||||||
|
|
||||||
As with most declarative libraries, there is an initial learning curve to
|
|
||||||
understand the concepts and usage. This tutorial tries to comprehensively
|
|
||||||
cover these concepts and usage, more so than you need just to use it.
|
|
||||||
|
|
|
||||||
|
|
@ -2,48 +2,33 @@
|
||||||
|
|
||||||
Sometimes you may need to do some cleanup when destroying a component or after
|
Sometimes you may need to do some cleanup when destroying a component or after
|
||||||
a side-effect from a source update. Vide provides a function `cleanup()` which
|
a side-effect from a source update. Vide provides a function `cleanup()` which
|
||||||
is used to queue a cleanup callback for the next time a reactive scope is rerun
|
is used to queue a callback for the next time a reactive scope is rerun or
|
||||||
or destroyed, or when a stable scope is destroyed.
|
destroyed, or when a stable scope is destroyed.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local mount = vide.mount
|
local root = vide.root
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local cleanup = vide.cleanup
|
local effect = vide.effect
|
||||||
|
|
||||||
local function Timer()
|
|
||||||
local count = source(0)
|
local count = source(0)
|
||||||
|
|
||||||
local con = game:GetService("RunService").Heartbeat:Connect(function(dt)
|
|
||||||
count(count() + dt)
|
local destroy = root(function(destroy)
|
||||||
|
effect(function()
|
||||||
|
local x = count()
|
||||||
|
cleanup(function() print(x) end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
cleanup(function()
|
cleanup(function() print "root destroyed" end)
|
||||||
con:Disconnect()
|
|
||||||
|
return destroy
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return create "TextButton" {
|
count(1) -- prints "0"
|
||||||
Position = UDim2.fromOffset(300, 300),
|
count(2) -- prints "1"
|
||||||
Size = UDim2.fromOffset(200, 50),
|
destroy() -- prints "2" and "root destroyed"
|
||||||
|
|
||||||
Text = function()
|
|
||||||
return "seconds: " .. math.floor(count())
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local instance, destroy = root(function(destroy)
|
|
||||||
local instance = Timer()
|
|
||||||
return instance, destroy
|
|
||||||
end)
|
|
||||||
|
|
||||||
wait(5)
|
|
||||||
|
|
||||||
destroy() -- all queued cleanups are ran, heartbeat connection disconnected
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above example, this allows us to disconnect the heartbeat connection
|
|
||||||
when the scope responsible for creating the timer component is destroyed.
|
|
||||||
|
|
||||||
::: tip
|
::: tip
|
||||||
Roblox instances do not need to be explicitly destroyed for their
|
Roblox instances do not need to be explicitly destroyed for their
|
||||||
memory to be freed, they only need to be parented to `nil`. So there is no
|
memory to be freed, they only need to be parented to `nil`. So there is no
|
||||||
|
|
|
||||||
|
|
@ -5,119 +5,26 @@ resulting from source updates. Vide provides functions to help you do this,
|
||||||
known as *control flow* functions.
|
known as *control flow* functions.
|
||||||
|
|
||||||
These functions return new sources, which hold the instances to be displayed.
|
These functions return new sources, which hold the instances to be displayed.
|
||||||
|
The new sources can be used in `create()` to update the children of a container
|
||||||
Control flow functions run their components in a new stable scope, which can
|
instance.
|
||||||
be destroyed independently of the stable scope that called the control flow
|
|
||||||
function. This means parts of your app can be independently created and
|
|
||||||
destroyed.
|
|
||||||
|
|
||||||
## switch()
|
|
||||||
|
|
||||||
`switch()` condtionally displays one instance at a time. It uses a table to map
|
|
||||||
a source value to a component.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local source = vide.source
|
|
||||||
local switch = vide.switch
|
|
||||||
|
|
||||||
local function Button(props: {
|
|
||||||
Text: string,
|
|
||||||
Activated: () -> ()
|
|
||||||
})
|
|
||||||
local hovered = source(false)
|
|
||||||
|
|
||||||
return create "TextButton" {
|
|
||||||
Text = props.Text,
|
|
||||||
Activated = props.Activated,
|
|
||||||
|
|
||||||
TextColor3 = function()
|
|
||||||
return hovered() and Color3.new(1, 1, 1) or Color3.new(.7, .7, .7)
|
|
||||||
end,
|
|
||||||
|
|
||||||
MouseEnter = function() hovered(true) end,
|
|
||||||
MouseLeave = function() hovered(false) end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function JoinMenu()
|
|
||||||
local joined = source(false)
|
|
||||||
|
|
||||||
local function JoinButton()
|
|
||||||
return Button {
|
|
||||||
Text = "Join",
|
|
||||||
Activated = function() joined(true) end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function LeaveButton()
|
|
||||||
return Button {
|
|
||||||
Text = "Leave"
|
|
||||||
Activated = function() joined(false) end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return create "Frame" {
|
|
||||||
switch(joined) {
|
|
||||||
[true] = LeaveButton,
|
|
||||||
[false] = JoinButton
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The reactive graph for the above example:
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {
|
|
||||||
"theme": "base",
|
|
||||||
"themeVariables": {
|
|
||||||
"primaryColor": "#1B1B1F",
|
|
||||||
"primaryTextColor": "#fff",
|
|
||||||
"primaryBorderColor": "#1B1B1F",
|
|
||||||
"lineColor": "#79B8FF",
|
|
||||||
"tertiaryColor": "#161618",
|
|
||||||
"tertiaryBorderColor": "#1C1C1F"
|
|
||||||
}
|
|
||||||
}}%%
|
|
||||||
|
|
||||||
graph
|
|
||||||
|
|
||||||
subgraph root["root scope"]
|
|
||||||
direction LR
|
|
||||||
joined --> switch -.- subroot
|
|
||||||
|
|
||||||
subgraph subroot["switch scope"]
|
|
||||||
direction LR
|
|
||||||
effect["TextColor3 effect"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
A `switch()` call creates a new effect and a new stable scope as seen in the
|
|
||||||
above graph. Whenever `menu` updates, it causes the `switch` effect to run,
|
|
||||||
which will destroy and recreate the switch scope with the new component.
|
|
||||||
|
|
||||||
This will also destroy the internal effect that the button uses to highlight
|
|
||||||
itself when it is hovered, each time the switch is rerun.
|
|
||||||
|
|
||||||
## indexes()
|
## indexes()
|
||||||
|
|
||||||
Often, you will have a table of values with each value displayed in a similar
|
`indexes()` *maps* each table index to a new UI element that can
|
||||||
manner. Rather than manually looping over each value to generate a corresponding
|
update to display the current value at that index. Each table index is given a
|
||||||
UI element, `indexes()` allows you to create elements each corresponding to a
|
single corresponding UI element.
|
||||||
table index, to display the value at that index.
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local todoList = source {
|
local list = source {
|
||||||
"finish the crash course",
|
"finish the crash course",
|
||||||
"star vide's GitHub"
|
"star Vide's GitHub"
|
||||||
}
|
}
|
||||||
|
|
||||||
local function TodoList(props: { list: () -> Array<string> })
|
local function TodoList(props: { list: () -> Array<string> })
|
||||||
return create "Frame" {
|
return create "Frame" {
|
||||||
create "UIListLayout" {},
|
create "UIListLayout" {},
|
||||||
|
|
||||||
indexes(todoList, function(todo, i)
|
indexes(list, function(todo, i)
|
||||||
return create "TextLabel" {
|
return create "TextLabel" {
|
||||||
Text = function()
|
Text = function()
|
||||||
return i .. ": " .. todo()
|
return i .. ": " .. todo()
|
||||||
|
|
@ -129,13 +36,13 @@ local function TodoList(props: { list: () -> Array<string> })
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
TodoList { list = todoList }
|
TodoList { list = list }
|
||||||
```
|
```
|
||||||
|
|
||||||
For each index in the given source table, the given function will be called
|
For each index in the given source table, the given function to `indexes()` will
|
||||||
with:
|
be run in a new stable scope with:
|
||||||
|
|
||||||
1. a source containing the value of the index
|
1. a source containing the value at the index
|
||||||
2. the index itself
|
2. the index itself
|
||||||
|
|
||||||
When the value at an index is changed, the function is not reran. Instead, the
|
When the value at an index is changed, the function is not reran. Instead, the
|
||||||
|
|
@ -143,12 +50,9 @@ given source for that index is updated.
|
||||||
|
|
||||||
Any time the input source table is updated, the given function will be ran for
|
Any time the input source table is updated, the given function will be ran for
|
||||||
any newly added indexes, while any removed indexes (indexes now with a `nil`
|
any newly added indexes, while any removed indexes (indexes now with a `nil`
|
||||||
value), will have its corresponding reactive scope destroyed to clean up that
|
value), will have its corresponding stable scope destroyed.
|
||||||
element.
|
|
||||||
|
|
||||||
`indexes()` is said to *map* each table index to a new UI element that can
|
|
||||||
update to display the current value at that index. Each table index is given a
|
|
||||||
single corresponding UI element.
|
|
||||||
|
|
||||||
The reactive graph for the above example:
|
The reactive graph for the above example:
|
||||||
|
|
||||||
|
|
@ -183,8 +87,8 @@ subgraph root ["root scope"]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
One thing to note regarding table sources, is that when you edit a table in a
|
When you edit a table in a source, you must set that table again to actually
|
||||||
source, you must set that table again to actually update the source.
|
update the source.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local src = source { 1, 2 }
|
local src = source { 1, 2 }
|
||||||
|
|
@ -192,11 +96,3 @@ local data = src()
|
||||||
table.insert(data, 3) -- no effects will run
|
table.insert(data, 3) -- no effects will run
|
||||||
src(data) -- effects will run
|
src(data) -- effects will run
|
||||||
```
|
```
|
||||||
|
|
||||||
Together, these control flow functions cover the majority of cases where you
|
|
||||||
need to dynamically create and destroy parts of your UI.
|
|
||||||
|
|
||||||
If you need to do something that these control flow functions cannot, you can
|
|
||||||
always use `mount()` within an effect to dynamically create and destroy
|
|
||||||
components on your own terms. Just remember to use `cleanup()` to unmount when
|
|
||||||
the effect reruns.
|
|
||||||
|
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
# Nested Properties
|
|
||||||
|
|
||||||
Often when creating components from existing components, you can find yourself
|
|
||||||
repetitively passing through properties such as size or position.
|
|
||||||
|
|
||||||
Example below:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function Background(props: {
|
|
||||||
Color: Color3,
|
|
||||||
AnchorPoint: UDim2,
|
|
||||||
Position: UDim2,
|
|
||||||
Size: UDim2
|
|
||||||
})
|
|
||||||
return create "Frame" {
|
|
||||||
Color = props.Color
|
|
||||||
AnchorPoint = props.AnchorPoint,
|
|
||||||
Position = props.Position,
|
|
||||||
Size = props.Size
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Menu(props: {
|
|
||||||
Color = props.Color
|
|
||||||
AnchorPoint: UDim2,
|
|
||||||
Position: UDim2,
|
|
||||||
Size: UDim2
|
|
||||||
})
|
|
||||||
return Background {
|
|
||||||
Color = props.Color,
|
|
||||||
AnchorPoint = props.AnchorPoint,
|
|
||||||
Position = props.Position,
|
|
||||||
Size = props.Size
|
|
||||||
}
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
One way this can be avoided is by using *property nesting*. In Vide, passing a
|
|
||||||
table value inside `props` has special semantics. Any key with a table value is
|
|
||||||
not assigned like a property, instead the table is iterated and processed just
|
|
||||||
like the outer table is. Any properties in the nested table will be assigned
|
|
||||||
to the instance just the same.
|
|
||||||
|
|
||||||
Below is an example of how you can use this to pass groups of similar properties
|
|
||||||
together such as position and size, while also using typechecking.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
type Layout = {
|
|
||||||
Layout = {
|
|
||||||
Position: UDim2?,
|
|
||||||
Size: UDim2?,
|
|
||||||
AnchorPoint: Vector2?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Background(props: Layout & { Color: Color3 })
|
|
||||||
return create "Frame" {
|
|
||||||
Color = props.Color,
|
|
||||||
props.Layout
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function Menu(props: Layout & { Color: Color3 })
|
|
||||||
return Background {
|
|
||||||
Color = props.Color,
|
|
||||||
Layout = props.Layout
|
|
||||||
}
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Here we created a nested group with the key `Layout` that can accept
|
|
||||||
layout-related properties. Any name could be chosen for the key.
|
|
||||||
This allows us to write much more concise syntax that is also typecheckable.
|
|
||||||
|
|
||||||
In another example we use a key named `Children` to pass arrays of instances to
|
|
||||||
be parented.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
type Children = {
|
|
||||||
-- also can optionally pass a source that returns an array of children too
|
|
||||||
Children = Array<Instance> | () -> Array<Instance>
|
|
||||||
}
|
|
||||||
|
|
||||||
local function List(props: Children & Layout)
|
|
||||||
return create "Frame" {
|
|
||||||
props.Children,
|
|
||||||
props.Layout,
|
|
||||||
create "UIListLayout" {}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
List {
|
|
||||||
Layout = {
|
|
||||||
Position = UDim2.new()
|
|
||||||
},
|
|
||||||
|
|
||||||
Children = {
|
|
||||||
create "TextLabel" { Text = "1" },
|
|
||||||
create "TextLabel" { Text = "2" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Deeper nested properties are guaranteed to be set after shallower nested
|
|
||||||
properties, this can be used to create overridable default properties.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local function List(props: Children & Layout)
|
|
||||||
return create "Frame" {
|
|
||||||
props.Children,
|
|
||||||
props.Layout,
|
|
||||||
|
|
||||||
-- can be overriden by `props.Layout`
|
|
||||||
AnchorPoint = Vector2.new(0.5, 0),
|
|
||||||
Position = UDim2.fromScale(0.5, 0),
|
|
||||||
|
|
||||||
create "UIListLayout" {}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
@ -2,11 +2,8 @@
|
||||||
|
|
||||||
Instances are created using `create()`.
|
Instances are created using `create()`.
|
||||||
|
|
||||||
`create()` returns a constructor for a class which then takes a table of
|
Parentheses `()` can be omitted when calling functions with string or
|
||||||
properties to assign when creating a new instance for that class.
|
table literals which is recommended for brevity.
|
||||||
|
|
||||||
Luau allows us to omit parentheses `()` when calling functions with string or
|
|
||||||
table literals which is recommended to use for brevity.
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local create = vide.create
|
local create = vide.create
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,12 @@ end
|
||||||
return Button
|
return Button
|
||||||
```
|
```
|
||||||
|
|
||||||
```lua [App.luau]
|
```lua [Menu.luau]
|
||||||
local create = vide.create
|
local create = vide.create
|
||||||
|
|
||||||
local Button = require(Button)
|
local Button = require(Button)
|
||||||
|
|
||||||
local function App()
|
local function Menu()
|
||||||
return create "ScreenGui" {
|
return create "ScreenGui" {
|
||||||
Button {
|
Button {
|
||||||
Position = UDim2.fromOffset(200, 200),
|
Position = UDim2.fromOffset(200, 200),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Sources
|
# Sources
|
||||||
|
|
||||||
Sources are special objects that store a single value. They are the core of
|
Sources are special objects that store a single value and are the core of
|
||||||
Vide's reactivity. They are called sources because they act as sources of data.
|
Vide's reactivity.
|
||||||
|
|
||||||
A source can be created using `source()`.
|
A source can be created using `source()`.
|
||||||
|
|
||||||
|
|
@ -20,8 +20,7 @@ by calling it with no arguments.
|
||||||
count(count() + 1) -- increment count by 1
|
count(count() + 1) -- increment count by 1
|
||||||
```
|
```
|
||||||
|
|
||||||
Sources can be *derived* by wrapping them in functions. A wrapped source
|
Sources can be *derived* by wrapping them in functions.
|
||||||
effectively becomes a new source.
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local count = source(0)
|
local count = source(0)
|
||||||
|
|
@ -35,7 +34,5 @@ count(1)
|
||||||
print(text()) -- "count: 1"
|
print(text()) -- "count: 1"
|
||||||
```
|
```
|
||||||
|
|
||||||
Sources on their own aren't very special, the above can be achieved with plain
|
While the above can be achieved with plain variables, the use for sources will
|
||||||
variables. The real use for sources become apparent when used in combination
|
be obvious in the next part.
|
||||||
with *effects*. Similar to a signal and connection, a source and effect allows
|
|
||||||
you to do things like automatically updating UI when a source is updated.
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
# Effects
|
# Effects
|
||||||
|
|
||||||
Effects are functions that are ran in response to source updates. They are
|
Effects are functions that are ran in response to source updates. They are
|
||||||
called effects because they cause *side-effects* when reacting to source
|
A source and effect is analogous to a signal and connection.
|
||||||
updates.
|
|
||||||
|
|
||||||
Effects are created using `effect()`.
|
Effects are created using `effect()`.
|
||||||
|
|
||||||
|
|
@ -21,11 +20,10 @@ count(1)
|
||||||
-- "count: 1" printed
|
-- "count: 1" printed
|
||||||
```
|
```
|
||||||
|
|
||||||
The callback given to `effect()` is ran immediately in a *reactive scope*. Any
|
Any source read inside an effect is tracked and will rerun the effect when
|
||||||
source read from inside a reactive scope will be tracked, so when any of those
|
that source is updated.
|
||||||
sources update, the effect will be reran too.
|
|
||||||
|
|
||||||
Reactive scopes also track derived sources, it doesn't matter how deeply nested
|
Derived sources are also tracked, it doesn't matter how deeply nested
|
||||||
inside a function a source is.
|
inside a function a source is.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,29 @@
|
||||||
# Scopes
|
# Scopes
|
||||||
|
|
||||||
Vide operates on the concept of scopes. Vide scopes come in two flavors:
|
Just like how a signal's connection may need to be disconnected, a source's
|
||||||
stable and reactive.
|
effect also may need to be disconnected.
|
||||||
|
|
||||||
The three main rules for scopes are:
|
But the disconnecting of many signals and connections is tedious and verbose.
|
||||||
|
Vide instead operates on the concept of scopes which provides a much cleaner
|
||||||
|
API, given that you follow a few rules.
|
||||||
|
|
||||||
- Stable scopes never rerun.
|
Scopes come in two flavors; stable and reactive.
|
||||||
- Reactive scopes will rerun on source updates.
|
|
||||||
- A reactive scope cannot be created within another reactive scope.
|
|
||||||
|
|
||||||
Reactive scopes cannot be created on their own - they must be created within
|
- All scopes must be created within another scope with the exception of `root()`
|
||||||
a stable scope so that it can be tracked and later destroyed when it is
|
- Stable scopes never rerun
|
||||||
no longer needed.
|
- Reactive scopes can rerun
|
||||||
|
- A reactive scope cannot be created within another reactive scope
|
||||||
|
|
||||||
This is the purpose of `root()`, which creates an initial stable scope, which
|
`effect()` creates a reactive scope.
|
||||||
all other reactive scopes, such as ones created by `effect()`, can stem from.
|
`root()` creates a stable scope.
|
||||||
|
|
||||||
When this root reactive scope is destroyed, it will destroy any effects created
|
Whenever a scope is destroyed, any scope created within that scope is also
|
||||||
within it, ensuring everything is cleaned up properly.
|
destroyed, and so on. This is why all scopes must be created within another
|
||||||
|
scope, except `root()` which is used to create the initial scope that you can
|
||||||
|
manually destroy.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
local root = vide.root
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local effect = vide.effect
|
local effect = vide.effect
|
||||||
|
|
||||||
|
|
@ -33,9 +37,9 @@ local function setup()
|
||||||
return count
|
return count
|
||||||
end
|
end
|
||||||
|
|
||||||
setup() -- will error since effect() was not called within a stable scope
|
setup() -- will error since effect() tries to create a reactive scope outside of a stable scope
|
||||||
|
|
||||||
local count = vide.root(setup) -- runs
|
local count = root(setup) -- ok since effect() was called within a stable scope
|
||||||
count(1) -- prints "1"
|
count(1) -- prints "1"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -87,7 +91,7 @@ subgraph root
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
When the root reactive scope created by `root()` is destroyed, the `effect`
|
When the stable `root()` is destroyed, the reactive `effect()`
|
||||||
scope will also be destroyed since it was created within it.
|
scope will also be destroyed since it was created within it.
|
||||||
|
|
||||||
This is important because you may have an effect that updates the property of a
|
This is important because you may have an effect that updates the property of a
|
||||||
|
|
@ -96,7 +100,6 @@ memory. The effect being destroyed will remove this reference, allowing the
|
||||||
instance to be garbage collected.
|
instance to be garbage collected.
|
||||||
|
|
||||||
You don't need to worry about ensuring all your effects are created within a
|
You don't need to worry about ensuring all your effects are created within a
|
||||||
root reactive scope, since you should be creating all your UI and corresponding
|
stable scope, since you should be creating all your UI and effects within a
|
||||||
effects within a top-level `root()` call that puts all your UI together. So it
|
single top-level `root()` call that puts all your UI together, making it safe to
|
||||||
is safe to assume that any effect you create will be created under this top
|
assume any effect created will be created under this stable scope.
|
||||||
level scope. Vide will prevent you from accidently doing otherwise anyways.
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
# Stateful Components
|
# Stateful Components
|
||||||
|
|
||||||
A stateful component is a component that stores some data internally.
|
|
||||||
|
|
||||||
Stateful components in Vide are created using sources and effects - sources to
|
Stateful components in Vide are created using sources and effects - sources to
|
||||||
store the data, and effects to display the data.
|
store the data, and effects to display the data.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Implicit Effects
|
# Implicit Effects
|
||||||
|
|
||||||
Explicitly creating effects to update properties can be tedious. Vide provides a
|
Explicitly creating effects to update properties is tedious. You can
|
||||||
way to *implicitly* create an effect to update properties.
|
*implicitly* create an effect to update properties instead.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local create = vide.create
|
local create = vide.create
|
||||||
|
|
@ -25,18 +25,14 @@ end
|
||||||
This example is equivalent to the example seen on the previous page.
|
This example is equivalent to the example seen on the previous page.
|
||||||
|
|
||||||
Instead of explicitly creating an effect, assigning a (non-event) property a
|
Instead of explicitly creating an effect, assigning a (non-event) property a
|
||||||
function will implicitly create an effect to update that property anytime a
|
function will implicitly create an effect to update that property.
|
||||||
source used within is updated.
|
|
||||||
|
|
||||||
Just like effects, the function is ran immediately in a reactive scope to set
|
|
||||||
the property initially and determine what sources are being used.
|
|
||||||
|
|
||||||
## Children
|
## Children
|
||||||
|
|
||||||
Children can also be set in a similar manner. A source passed as a child (passed
|
Children can also be set in a similar manner. A source passed as a child (passed
|
||||||
with a number key instead of string key) can return an instance or an array of
|
with a number key instead of string key) can return an instance or an array of
|
||||||
instances. Vide will automatically unparent removed instances and parent new
|
instances. An effect is automatically created to unparent removed instances and
|
||||||
instances when that source's stored instances change.
|
parent new instances on source update.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local items = source {
|
local items = source {
|
||||||
|
|
@ -57,5 +53,5 @@ items {
|
||||||
create "TextLabel" { Text = "C" }
|
create "TextLabel" { Text = "C" }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- this will automatically unparent the text label "A", and parent the labels "B" and "C".
|
-- this will automatically unparent the text label "A", and parent the labels "B" and "C"
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,13 @@ source(1) -- prints "ran" x2
|
||||||
```
|
```
|
||||||
|
|
||||||
To avoid this, you can use `derive()` to derive a new source instead. This will
|
To avoid this, you can use `derive()` to derive a new source instead. This will
|
||||||
run a function in a new reactive scope only when a dependent source has updated.
|
run a function in a reactive scope only when a source used inside updated.
|
||||||
Reading this derived source multiple times will just return a cached result from
|
Reading this derived source multiple times will just return a cached result.
|
||||||
when it last updated.
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local derive = vide.derive
|
|
||||||
local effect = vide.effect
|
local effect = vide.effect
|
||||||
|
local derive = vide.derive
|
||||||
|
|
||||||
local count = source(0)
|
local count = source(0)
|
||||||
|
|
||||||
|
|
@ -87,6 +86,7 @@ end
|
||||||
```
|
```
|
||||||
|
|
||||||
Deriving a source in this manner is similar to creating an effect to update
|
Deriving a source in this manner is similar to creating an effect to update
|
||||||
another source. You should never manually do this using an effect however,
|
another source. You should never manually do this using an effect however.
|
||||||
improper usage could accidently create infinite loops in the reactive graph.
|
Improper usage could accidently create infinite loops in the reactive graph.
|
||||||
Always favour deriving when you need one source to update based on another.
|
Always favour deriving when you need one source to update based on another
|
||||||
|
source.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue