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
9b6be14441
commit
46937c6979
16 changed files with 157 additions and 116 deletions
|
|
@ -22,8 +22,9 @@ Currently, strict mode will:
|
||||||
6. Checks for duplicate nested properties at same depth.
|
6. Checks for duplicate nested properties at same depth.
|
||||||
7. Better error reporting and stack traces + creation traces of property bindings.
|
7. Better error reporting and stack traces + creation traces of property bindings.
|
||||||
|
|
||||||
By rerunning sources and effects, any side-effects are made more apparent.
|
By rerunning derived sources and effects twice each time they update,it helps
|
||||||
This also helps ensure that cleanups are being handled correctly.
|
ensure that derived source computations are pure, and that any
|
||||||
|
cleanups made in derived sources or effects are done correctly.
|
||||||
|
|
||||||
Accidental yielding within reactive scopes can break Vide's reactive graph,
|
Accidental yielding within reactive scopes can break Vide's reactive graph,
|
||||||
which strict mode can catch.
|
which strict mode can catch.
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ This code will produce a graph that looks like so:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart
|
graph
|
||||||
subgraph root
|
subgraph root
|
||||||
forename & surname --> name
|
forename & surname --> name
|
||||||
name --> effect
|
name --> effect
|
||||||
|
|
@ -145,7 +145,7 @@ This code produces a graph like so:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart LR
|
graph LR
|
||||||
subgraph root
|
subgraph root
|
||||||
counters --> indexes
|
counters --> indexes
|
||||||
|
|
||||||
|
|
@ -208,7 +208,7 @@ The above code produces a graph like so:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart LR
|
graph LR
|
||||||
subgraph root
|
subgraph root
|
||||||
direction LR
|
direction LR
|
||||||
show
|
show
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ 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 GUI.
|
||||||
|
|
||||||
## Why Vide?
|
## Why Vide?
|
||||||
|
|
||||||
Creating UI is a slow and tedious process. The purpose of Vide is to make UI
|
Creating UI is a slow and tedious process. The purpose of Vide is to make UI
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ is used to register a cleanup callback for the next time the reactive scope
|
||||||
it is called in re-runs.
|
it is called in re-runs.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
locla mount = vide.mount
|
local mount = vide.mount
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
local cleanup = vide.cleanup
|
local cleanup = vide.cleanup
|
||||||
|
|
||||||
|
|
@ -60,11 +60,11 @@ The reactive graph for the above example:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart
|
graph
|
||||||
|
|
||||||
subgraph root
|
subgraph mount
|
||||||
direction LR
|
direction LR
|
||||||
cleanup([cleanup]) ~~~ count
|
cleanup([cleanup]) ~~~ count
|
||||||
count --> bind[text binding]
|
count --> bind["effect (text binding)"]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ will update when the input source updates.
|
||||||
Control flow functions are special, because they run their components in a new
|
Control flow functions are special, because they run their components in a new
|
||||||
reactive scope, which can be destroyed independently of the reactive scope that
|
reactive scope, which can be destroyed independently of the reactive scope that
|
||||||
called the control flow function itself. This means that parts of your app can
|
called the control flow function itself. This means that parts of your app can
|
||||||
be independently created then destroyed and cleaned.
|
be independently created then destroyed.
|
||||||
|
|
||||||
## show()
|
## show()
|
||||||
|
|
||||||
|
|
@ -78,7 +78,7 @@ The reactive graph for the above example:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart
|
graph
|
||||||
|
|
||||||
subgraph root["mount() scope"]
|
subgraph root["mount() scope"]
|
||||||
direction LR
|
direction LR
|
||||||
|
|
@ -91,8 +91,11 @@ subgraph root ["mount() scope"]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
The dotted line indicates that the new reactive scope isn't actually connected
|
`show()` will implicitly create an effect depending on `joined`, which can be
|
||||||
to the `show` on the graph, it is only managed internally through code.
|
seen as `show` on the graph. This effect manages, and can create or destroy
|
||||||
|
a separate reactive scope seen as `show() scope` on the graph. The dotted line
|
||||||
|
indicates that it isn't actually connected, only indirectly managed through
|
||||||
|
code.
|
||||||
|
|
||||||
## switch()
|
## switch()
|
||||||
|
|
||||||
|
|
@ -135,11 +138,11 @@ The switch can map any value to any component.
|
||||||
```lua
|
```lua
|
||||||
type ActiveMenu = "none" | "inventory" | "shop" | "settings"
|
type ActiveMenu = "none" | "inventory" | "shop" | "settings"
|
||||||
|
|
||||||
local menu = source "none"
|
local menu = source "inventory"
|
||||||
|
|
||||||
switch(menu) {
|
switch(menu) {
|
||||||
inventory = InventoryMenu,
|
inventory = InventoryMenu,
|
||||||
shop = ShopMenu.
|
shop = ShopMenu,
|
||||||
settings = SettingsMenu
|
settings = SettingsMenu
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -159,25 +162,25 @@ The reactive graph for the above example:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart
|
graph
|
||||||
|
|
||||||
subgraph root["mount() scope"]
|
subgraph root["mount() scope"]
|
||||||
direction LR
|
direction LR
|
||||||
joined --> show -.- subroot
|
menu --> switch -.- subroot
|
||||||
|
|
||||||
subgraph subroot["switch() scope"]
|
subgraph subroot["switch() scope"]
|
||||||
direction LR
|
direction LR
|
||||||
Button
|
Menu
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
## indexes()
|
## indexes()
|
||||||
|
|
||||||
Often, you will have a table of values that will be displayed in a similar
|
Often, you will have a table of values with each value displayed in a similar
|
||||||
manner. Rather than manually looping over each value to generate a corresponding
|
manner. Rather than manually looping over each value to generate a corresponding
|
||||||
UI element, `indexes()` allows you to create an instance for each table index,
|
UI element, `indexes()` allows you to create elements for each table index, to
|
||||||
to display the value at that index.
|
display the value at that index.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local todoList = source {
|
local todoList = source {
|
||||||
|
|
@ -204,13 +207,22 @@ end
|
||||||
TodoList { list = todoList }
|
TodoList { list = todoList }
|
||||||
```
|
```
|
||||||
|
|
||||||
For each unique index in the passed table, the transform function will be called
|
For each index in the given source table, the given function will be called
|
||||||
with 1. a source containing the value of the index, 2. the index itself.
|
with:
|
||||||
|
|
||||||
|
1. a source containing the value of the index
|
||||||
|
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
|
||||||
given source for that index is updated.
|
given source for that index is updated.
|
||||||
|
|
||||||
An element is only destroyed if the value of an index is set to `nil`.
|
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`
|
||||||
|
value), will have its corresponding reactive scope destroyed to clean up that
|
||||||
|
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.
|
||||||
|
|
||||||
The reactive graph for the above example:
|
The reactive graph for the above example:
|
||||||
|
|
||||||
|
|
@ -227,7 +239,7 @@ The reactive graph for the above example:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart
|
graph
|
||||||
|
|
||||||
subgraph root ["mount() scope"]
|
subgraph root ["mount() scope"]
|
||||||
direction LR
|
direction LR
|
||||||
|
|
@ -245,5 +257,20 @@ subgraph root ["mount() scope"]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
One thing to note regarding table sources, is that when you edit a table in a
|
||||||
|
source, you must set that table again to actually update the source.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local src = source { 1, 2 }
|
||||||
|
local data = src()
|
||||||
|
table.insert(data, 3) -- no effects will run
|
||||||
|
src(data) -- effects will run
|
||||||
|
```
|
||||||
|
|
||||||
Together, these control flow functions cover the majority of cases where you
|
Together, these control flow functions cover the majority of cases where you
|
||||||
need to dynamically create and destroy parts of your UI.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ be parented.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
type Children = {
|
type Children = {
|
||||||
-- allows us to also optionally pass a source that returns an array of children instead
|
-- also can optionally pass a source that returns an array of children too
|
||||||
Children = Array<Instance> | () -> Array<Instance>
|
Children = Array<Instance> | () -> Array<Instance>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
# Actions
|
# Actions
|
||||||
|
|
||||||
Actions in Vide are special callbacks that you can pass along with properties,
|
Actions in Vide are special callbacks that you can pass along with properties,
|
||||||
which will be called when those properties are being processed with the instance
|
to run some code on an instance receiving them.
|
||||||
being assigned to, allowing you to run custom code.
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local action = vide.action
|
local action = vide.action
|
||||||
|
|
@ -20,24 +19,22 @@ create "TextLabel" {
|
||||||
-- will print "test"
|
-- will print "test"
|
||||||
```
|
```
|
||||||
|
|
||||||
Actions can be wrapped with functions to re-use specific behaviors. Below is
|
Actions can be wrapped with functions for reuse. Below is an example of an
|
||||||
an example of an action used to listen for property changes:
|
action used to listen for property changes:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local action = vide.action
|
local action = vide.action
|
||||||
local cleanup = vide.cleanup
|
local cleanup = vide.cleanup
|
||||||
|
|
||||||
local function changed(property: string, callback: (new) -> ())
|
local function changed(prop: string, callback: (new) -> ())
|
||||||
return action(function(instance)
|
return action(function(instance)
|
||||||
local con = instance:GetPropertyChangedSignal(property):Connect(function()
|
local connection = instance:GetPropertyChangedSignal(prop):Connect(function()
|
||||||
callback(instance[property])
|
callback(instance[property])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- remember to clean up the connection when the reactive scope the action
|
-- remember to clean up the connection when the reactive scope the action
|
||||||
-- is ran in is destroyed, so the instance can be garbage collected
|
-- is ran in is destroyed, so the instance can be garbage collected
|
||||||
cleanup(function()
|
cleanup(connection)
|
||||||
con:Disconnect()
|
|
||||||
end)
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,13 @@ be set with `vide.strict = true` once when you first require Vide. Strict mode
|
||||||
will add extra safety checks and emit better error traces, particularly when
|
will add extra safety checks and emit better error traces, particularly when
|
||||||
errors occur in property bindings.
|
errors occur in property bindings.
|
||||||
|
|
||||||
|
Strict mode is automatically enabled when Vide is required in O0 or O1
|
||||||
|
optimization (default studio level). You can `vide.strict = false` if you do not
|
||||||
|
want this.
|
||||||
|
|
||||||
Strict mode will run derived sources and effects twice each time they update.
|
Strict mode will run derived sources and effects twice each time they update.
|
||||||
This is to help identify improper cleanup of side-effects and ensure that pure
|
This is to help ensure that derived source computations are pure, and that any
|
||||||
computations are actually pure.
|
cleanups made in derived sources or effects are done correctly.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
|
|
|
||||||
|
|
@ -45,19 +45,10 @@ Assign a value to a string key to set a property, and assign a value to a
|
||||||
number key to set a child. Events can be connected to by assigning a function
|
number key to set a child. Events can be connected to by assigning a function
|
||||||
to a string key.
|
to a string key.
|
||||||
|
|
||||||
You can also use a shorthand to create datatypes instead of explicitly typing
|
::: warning
|
||||||
out the class name and constructor. The table will be unpacked into the `.new()`
|
|
||||||
constructor of the property's type.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
create "Frame" {
|
|
||||||
AnchorPoint = { 0.5, 1 },
|
|
||||||
UDim2 = { 0.5, 0, 0.5, 0 }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When creating an instance with no properties, it is important to not forget to
|
When creating an instance with no properties, it is important to not forget to
|
||||||
actually call the constructor: `create "Frame" {}` and not `create "Frame"`.
|
actually call the constructor: `create "Frame" {}` and not `create "Frame"`.
|
||||||
To be clear, `create "Frame"` returns a *function* which is a constructor for
|
To be clear, `create "Frame"` returns a *function* which is a constructor for
|
||||||
that class, not an instance of that class. This would result in you attempting
|
that class, not an instance of that class. This would result in you attempting
|
||||||
to parent a function instead of an instance which is not the correct behavior.
|
to parent a function instead of an instance which is not correct.
|
||||||
|
:::
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
# Components
|
# Components
|
||||||
|
|
||||||
Components are custom-made reusable pieces of UI made from other pieces of UI.
|
A component is a function that creates and returns a piece of UI.
|
||||||
|
|
||||||
By using components you can make your application more modular and better
|
This is a way to separate your app into small chunks that you can reuse and put
|
||||||
organized.
|
together.
|
||||||
|
|
||||||
|
::: code-group
|
||||||
|
|
||||||
```lua [Button.luau]
|
```lua [Button.luau]
|
||||||
local create = vide.create
|
local create = vide.create
|
||||||
|
|
@ -15,11 +17,14 @@ local function Button(props: {
|
||||||
})
|
})
|
||||||
return create "TextButton" {
|
return create "TextButton" {
|
||||||
BackgroundColor3 = Color3.fromRGB(50, 50, 50),
|
BackgroundColor3 = Color3.fromRGB(50, 50, 50),
|
||||||
|
TextColor3 = Color3.fromRGB(255, 255, 255),
|
||||||
Size = UDim2.fromOffset(200, 150),
|
Size = UDim2.fromOffset(200, 150),
|
||||||
|
|
||||||
Position = props.Position,
|
Position = props.Position,
|
||||||
Text = props.Text,
|
Text = props.Text,
|
||||||
Activated = props.Activated
|
Activated = props.Activated,
|
||||||
|
|
||||||
|
create "UICorner" {}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -36,10 +41,17 @@ local function App()
|
||||||
return create "ScreenGui" {
|
return create "ScreenGui" {
|
||||||
Button {
|
Button {
|
||||||
Position = UDim2.fromOffset(200, 200),
|
Position = UDim2.fromOffset(200, 200),
|
||||||
Text = "click me!",
|
Text = "back",
|
||||||
|
|
||||||
Activated = function()
|
Activated = function()
|
||||||
print "clicked"
|
print "go to previous page"
|
||||||
|
end
|
||||||
|
},
|
||||||
|
|
||||||
|
Button {
|
||||||
|
Position = UDim2.fromOffset(400, 200),
|
||||||
|
Text = "next",
|
||||||
|
Activated = function()
|
||||||
|
print "go to next page"
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,8 +60,9 @@ end
|
||||||
mount(App, game.StarterGui)
|
mount(App, game.StarterGui)
|
||||||
```
|
```
|
||||||
|
|
||||||
Above is a simple example of a button component with a set color and size,
|
:::
|
||||||
being reused across files.
|
|
||||||
|
Above is a simple example of a button component being used across files.
|
||||||
|
|
||||||
A single parameter `props` is used to pass properties to the component.
|
A single parameter `props` is used to pass properties to the component.
|
||||||
|
|
||||||
|
|
@ -57,8 +70,8 @@ Components allow you to *encapsulate* behavior. You can only modify the
|
||||||
component in ways that you allow in the component, through the `props` parameter.
|
component in ways that you allow in the component, through the `props` parameter.
|
||||||
|
|
||||||
To create a new button all you must do is call the `Button` function, passing in
|
To create a new button all you must do is call the `Button` function, passing in
|
||||||
values through props. This saves having to create and set every property each
|
values. This saves having to create and set every property each time. Also, when
|
||||||
time. Also, when updating the button component in future, any changes to the
|
updating the button component in future, any changes to the button file will be
|
||||||
button file will be seen anywhere the button is used in your app.
|
seen anywhere the button is used in your app.
|
||||||
|
|
||||||
This can be extended to much more complicated UI.
|
This can be extended to much more complicated UI.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
# Sources
|
# Sources
|
||||||
|
|
||||||
*Sources* in Vide are special objects that store a single value. They are the
|
Sources are special objects that store a single value. They are the core of
|
||||||
core of reactivity in Vide. Each source represents a source of data, and they
|
Vide's reactivity. They are called sources because they act as sources of data.
|
||||||
can be composed and derived to create new sources of data.
|
|
||||||
|
|
||||||
A source in Vide can be created using `source()`.
|
A source can be created using `source()`.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,9 @@ count(1)
|
||||||
-- "count: 1" printed
|
-- "count: 1" printed
|
||||||
```
|
```
|
||||||
|
|
||||||
The callback given to `effect()` is ran in a *reactive scope*. Any source read
|
The callback given to `effect()` is initially ran immediately in a
|
||||||
from inside a reactive scope will be tracked, so that if any of those sources
|
*reactive scope*. Any source read from inside a reactive scope will be tracked,
|
||||||
update, the effect will be reran too.
|
so that if any of those sources update, the effect will be reran too.
|
||||||
|
|
||||||
The callback is first ran immediately inside the `effect()` call to initially
|
|
||||||
track sources used.
|
|
||||||
|
|
||||||
Effects also work with derived sources, it doesn't matter how deeply nested
|
Effects also work with derived sources, it doesn't matter how deeply nested
|
||||||
inside a function a source is.
|
inside a function a source is.
|
||||||
|
|
@ -50,23 +47,5 @@ count(2)
|
||||||
-- "doubled count: 4" printed
|
-- "doubled count: 4" printed
|
||||||
```
|
```
|
||||||
|
|
||||||
The reactive graph for the above example:
|
If a source is updated with the same value it already had, it will not rerun
|
||||||
|
effects depending on it.
|
||||||
```mermaid
|
|
||||||
%%{init: {
|
|
||||||
"theme": "base",
|
|
||||||
"themeVariables": {
|
|
||||||
"primaryColor": "#1B1B1F",
|
|
||||||
"primaryTextColor": "#fff",
|
|
||||||
"primaryBorderColor": "#1B1B1F",
|
|
||||||
"lineColor": "#79B8FF",
|
|
||||||
"tertiaryColor": "#161618",
|
|
||||||
"tertiaryBorderColor": "#fff"
|
|
||||||
}
|
|
||||||
}}%%
|
|
||||||
|
|
||||||
flowchart LR
|
|
||||||
|
|
||||||
count --> effect
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Root Reactive Scopes
|
# Root Reactive Scopes
|
||||||
|
|
||||||
Any reactive scopes created, such as one from `effect()`, must be done so within
|
Any reactive scopes created, such as by `effect()`, must be done so within a
|
||||||
a "root" reactive scope. This is the main purpose of `mount()`, which you use
|
"root" reactive scope. This is the main purpose of `mount()`, which you use
|
||||||
once at the top level to create your app as shown in the first introduction.
|
once at the top level to create your app as shown in the first introduction.
|
||||||
|
|
||||||
This is so that when the app is unmounted, it can clean up any reactive scopes
|
This is so that when the app is unmounted, it can clean up any reactive scopes
|
||||||
|
|
@ -25,7 +25,18 @@ vide.mount(App) -- works!
|
||||||
App() -- will error since effect() was not called within a reactive scope
|
App() -- will error since effect() was not called within a reactive scope
|
||||||
```
|
```
|
||||||
|
|
||||||
The reactive graph for the above example:
|
Mounting returns a function that when called will destroy any reactive scopes
|
||||||
|
created during the `mount()` call.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local unmount = mount(App)
|
||||||
|
|
||||||
|
unmount()
|
||||||
|
```
|
||||||
|
|
||||||
|
Vide's reactivity can be represented graphically, as a *reactive graph*.
|
||||||
|
|
||||||
|
The reactive graph for the above example looks like so:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%%{init: {
|
%%{init: {
|
||||||
|
|
@ -40,10 +51,18 @@ The reactive graph for the above example:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart
|
graph
|
||||||
|
|
||||||
subgraph root
|
subgraph root["mount"]
|
||||||
direction LR
|
direction LR
|
||||||
count --> effect
|
count --> effect
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When the `mount` scope is destroyed, the `effect` scope will also be destroyed
|
||||||
|
since it was created within it.
|
||||||
|
|
||||||
|
You don't need to worry about ensuring all your effects are created within a
|
||||||
|
root scope, since you should be creating all your UI and corresponding effects
|
||||||
|
within a top-level `mount()` call that puts all your UI together. So it is safe
|
||||||
|
to assume that any effect you create will be created under this top level scope.
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ A stateful component is a component that stores and displays some data.
|
||||||
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.
|
||||||
|
|
||||||
|
## Internal State
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local create = vide.create
|
local create = vide.create
|
||||||
local source = vide.source
|
local source = vide.source
|
||||||
|
|
@ -30,12 +32,14 @@ end
|
||||||
Above is an example of a counter component, that when clicked, will increment
|
Above is an example of a counter component, that when clicked, will increment
|
||||||
its internal count, and automatically update its text to reflect that count.
|
its internal count, and automatically update its text to reflect that count.
|
||||||
|
|
||||||
Making a property update based on a source is also called *property
|
Making a property update based on a source is also referred to as *property
|
||||||
binding*.
|
binding*.
|
||||||
|
|
||||||
Each instance of `Counter()` will maintain its own independent count, since the
|
Each instance of `Counter()` will maintain its own independent count, since the
|
||||||
count source is created inside the scope of the component.
|
count source is created inside the scope of the component.
|
||||||
|
|
||||||
|
## External State
|
||||||
|
|
||||||
External sources can also be passed into components for them to use.
|
External sources can also be passed into components for them to use.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|
@ -65,5 +69,5 @@ count(1) -- the Counter component will update to display this count
|
||||||
```
|
```
|
||||||
|
|
||||||
Sources can be created internally or passed in from externally, there are no
|
Sources can be created internally or passed in from externally, there are no
|
||||||
restrictions on how they are used as long as the effect is created within a
|
restrictions on how they are used as long as the effect using it is created
|
||||||
reactive scope so that it can be tracked.
|
within a reactive scope so that it can be cleaned up later.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ local function Counter()
|
||||||
count(count() + 1)
|
count(count() + 1)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
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.
|
||||||
|
|
@ -32,15 +33,16 @@ Just like effects, the function is ran immediately in a reactive scope to set
|
||||||
the property initially and determine what sources are being depended on.
|
the property initially and determine what sources are being depended on.
|
||||||
|
|
||||||
This allows you as the programmer to not need to manually update UI as the state
|
This allows you as the programmer to not need to manually update UI as the state
|
||||||
of your program changes. You just define how the data maps to UI, and Vide's
|
of your program changes. You just define how the data sources map to UI, and
|
||||||
reactive system will automatically update any properties depending on sources
|
Vide's reactive system will automatically update any properties depending on
|
||||||
that are updated.
|
those sources that were updated.
|
||||||
|
|
||||||
## Children Binding
|
## Children Binding
|
||||||
|
|
||||||
Children can also be set in a similar manner. Sources bound to properties can
|
Children can also be set in a similar manner. A source passed as a child (passed
|
||||||
return an instance or an array of instances. Vide will automatically unparent
|
with a number key instead of string key) can return an instance or an array of
|
||||||
removed instances and parent new instances.
|
instances. Vide will automatically unparent removed instances and parent new
|
||||||
|
instances when that source's stored instances change.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local items = source {
|
local items = source {
|
||||||
|
|
|
||||||
|
|
@ -60,17 +60,15 @@ effect(function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
effect(function()
|
effect(function()
|
||||||
text() -- does not print
|
text() -- does not print, returns cached value
|
||||||
end)
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
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,
|
|
||||||
improper usage could accidently create infinite loops in the reactive graph.
|
|
||||||
Always favour deriving when you need one source to update based on another.
|
|
||||||
|
|
||||||
`derive()` must also be used within a root reactive scope, just like `effect()`.
|
`derive()` must also be used within a root reactive scope, just like `effect()`.
|
||||||
|
|
||||||
|
If the recalculated value is the same as the old value, the derived source will
|
||||||
|
not rerun the effects using it.
|
||||||
|
|
||||||
The reactive graph for the above example:
|
The reactive graph for the above example:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
|
|
@ -86,10 +84,15 @@ The reactive graph for the above example:
|
||||||
}
|
}
|
||||||
}}%%
|
}}%%
|
||||||
|
|
||||||
flowchart
|
graph
|
||||||
|
|
||||||
subgraph root
|
subgraph root
|
||||||
direction LR
|
direction LR
|
||||||
count --> text --> effect1 & effect2
|
count --> text --> effect1 & effect2
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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,
|
||||||
|
improper usage could accidently create infinite loops in the reactive graph.
|
||||||
|
Always favour deriving when you need one source to update based on another.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue