2.6 KiB
Effects
Effects are functions that are ran in response to source updates. They are called effects because they cause side-effects when reacting to source updates.
Effects are created using effect().
local source = vide.source
local effect = vide.effect
local count = source(0)
effect(function()
print("count: " .. count())
end)
-- "count: 0" printed
count(1)
-- "count: 1" printed
The callback given to effect() is ran in a reactive scope. Any source read
from inside a reactive scope will be tracked, 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
figure out what sources are being used.
Effects also work with derived sources, it doesn't matter how deeply nested a source is.
local source = vide.source
local effect = vide.effect
local count = source(1)
local doubled = function()
return count() * 2
end
effect(function()
print("doubled count: " .. doubled())
end)
-- "doubled count: 2" printed
count(2)
-- "doubled count: 4" printed
Derived sources should be a pure computation. A pure computation is one where the same input will always produce the same output.
All observable changes to the user are considered to be side-effects of pure computations.
Sources, derived sources, and effects form what is called a reactive graph. In the above example, the following graph is formed. Anywhere an update occures, everything further down the graph is updated.
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#1B1B1F",
"primaryTextColor": "#fff",
"primaryBorderColor": "#1B1B1F",
"lineColor": "#79B8FF",
"tertiaryColor": "#161618",
"tertiaryBorderColor": "#161618"
}
}}%%
flowchart LR
count --> doubled --> effect
You should not update other sources using an effect. Improper usage can lead to a cyclic loop in the graph, causing an infinite loop when it tries to update. Sources should be derived instead.
Root Reactive Scopes
Effects must be created within another reactive scope. This is so that the
effect itself can be tracked and later freed when the parent reactive scope is
destroyed, such as from unmounting an app. The example code above will not
actually work unless it is ran inside a root reactive scope, such as one created
by vide.mount(function). This generally isn't a concern since you can assume
that all your components will be created within a single mount() call, which
happens only once at the top level, where you put together your UI and parent it
to a ScreenGUI.