Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
centau committed Jun 20, 2024
1 parent e1d69af commit fe31d8f
Show file tree
Hide file tree
Showing 25 changed files with 315 additions and 364 deletions.
4 changes: 2 additions & 2 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ export default withMermaid({
{ text: "Components", link: "/tut/crash-course/3-components" },
{ text: "Sources", link: "/tut/crash-course/4-source" },
{ text: "Effects", link: "/tut/crash-course/5-effect" },
{ text: "Root Scopes", link: "/tut/crash-course/6-root" },
{ text: "Scopes", link: "/tut/crash-course/6-scope" },
{ text: "Stateful Components", link: "/tut/crash-course/7-stateful-component" },
{ text: "Property Binding", link: "/tut/crash-course/8-property-binding" },
{ text: "Property Binding", link: "/tut/crash-course/8-implicit-effect" },
{ text: "Derived Sources", link: "/tut/crash-course/9-derived-source" },
{ text: "Cleanup", link: "/tut/crash-course/10-cleanup" },
{ text: "Control Flow", link: "/tut/crash-course/11-control-flow" },
Expand Down
2 changes: 1 addition & 1 deletion docs/api/creation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## mount()

Runs a function in a new reactive scope and optionally applies its result to a
Runs a function in a new stable scope and optionally applies its result to a
target instance.

- **Type**
Expand Down
9 changes: 5 additions & 4 deletions docs/api/reactivity-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
<br/>

:::warning
Yielding is not allowed in any reactive scope. Strict mode can check for this.
Yielding is not allowed in any stable or reactive scope. Strict mode will check
for this.
:::

## root()

Creates and runs a function in a new reactive scope.
Creates and runs a function in a new stable scope.

- **Type**

Expand All @@ -20,8 +21,8 @@ Creates and runs a function in a new reactive scope.

Returns the result of the given function.

Creates a new root reactive scope, where creation and derivations of sources
can be tracked and properly disposed of.
Creates a new stable scope, where creation of effects can be tracked and
properly disposed of.

A function to destroy the root is passed into the callback, which will run
any cleanups and allow derived sources created to garbage collect.
Expand Down
31 changes: 18 additions & 13 deletions docs/tut/advanced/nested-scoping.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Nested Reactive Scopes
# Nested Scopes

Nesting reactive scopes gives you finer control over the reactive graph, but
needs more work to do. The built-in control flow functions try to cover the
Nesting scopes gives you finer control over the reactive graph, but needs more work to do. The built-in control flow functions try to cover the
most common cases, but they do not cover all of them.

This tutorial will demonstrate how to implement a `show()` control flow function
Expand All @@ -21,7 +20,7 @@ local function Counter()
}
end

mount(function()
root(function()
local toggled = source(true)

show(toggled, Button)
Expand Down Expand Up @@ -57,7 +56,7 @@ Above is the reactive graph for `show()`. It creates a new effect depending on
`toggle` where anytime `toggle` is truthy, it will create a new `Counter`. The
`show` effect calls `Counter`, which creates a new reactive scope to update its
text whenever `count` changes. As per the rules of reactive scopes, a reactive
scope rerunning will destroy any reactive scope created within it. So the text
scope rerunning will destroy any scopes created within it. So the text
effect's reactive scope is destroyed whenever the show effect is rerun.

The same can be achieved without the use of `show()`:
Expand All @@ -82,7 +81,10 @@ mount(function()

effect(function()
if toggled() then
local destroy = mount(Button)
local destroy = root(function(destroy)
Counter()
return destroy
end)
cleanup(destroy)
end
end)
Expand Down Expand Up @@ -116,11 +118,14 @@ subgraph mount
end
```

This is another way to achieve the same. Here we use `mount()` within the effect
to manually create and destroy a new reactive scope whenever the effect reruns.
This is another way to achieve the same. Here we use `root()` within the effect
to manually create and destroy a new stable scope whenever the effect reruns.

The reason for creating a stable scope is to prevent the effect from tracking
any sources that may be read inside the `Counter()` call. Otherwise, the effect
may be rerun needlessly and recreate the counter.

Alternatively, instead of using `mount()`, a new reactive scope can be created
directly within the effect:
Alternatively, instead of using `root()`:

```lua
local mount = vide.mount
Expand Down Expand Up @@ -174,7 +179,9 @@ end
```

Without the use of `untrack()`, an error would occur, since Vide does not allow
the creation of reactive scopes inside reactive scopes that are tracking. The
the creation of reactive scopes inside reactive scopes. `untrack()` creates a
stable scope inside the reactive scope, and we can create another reactive scope
inside that stable scope. The
reason for this, is because if the `Counter` component reads from a source
internally, that can cause the reactive scope calling `Counter()` to track that
source, causing unintentional reruns. As a guard against this, you are forced to
Expand All @@ -184,5 +191,3 @@ The final result is the same as using the `show()` component. An effect is
created which creates the counter, which creates its own reactive scope. The
effect rerunning causes the counter's internal reactive scope to be destroyed,
making sure everything is cleaned up.


14 changes: 9 additions & 5 deletions docs/tut/crash-course/10-cleanup.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
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
is used to queue a cleanup callback for the next time a reactive scope is rerun
or destroyed.
or destroyed, or when a stable scope is destroyed.

```lua
local mount = vide.mount
Expand Down Expand Up @@ -31,14 +31,18 @@ local function Timer()
}
end

local unmount = mount(Timer)
local instance, destroy = root(function(destroy)
local instance = Timer()
return instance, destroy
end)

unmount() -- all queued cleanups are ran, heartbeat connection disconnected
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 reactive scope responsible for creating the timer component is
destroyed, such as when it is unmounted.
when the scope responsible for creating the timer component is destroyed.

::: tip
Roblox instances do not need to be explicitly destroyed for their
Expand Down
10 changes: 5 additions & 5 deletions docs/tut/crash-course/11-control-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ known as *control flow* functions.

These functions return new sources, which hold the instances to be displayed.

Control flow functions run their components in a new reactive scope, which can
be destroyed independently of the reactive scope that called the control flow
Control flow functions run their components in a new stable scope, which can
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.

Expand Down Expand Up @@ -93,9 +93,9 @@ subgraph root["root scope"]
end
```

A `switch()` call creates a new effect and a new 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.
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.
Expand Down
2 changes: 1 addition & 1 deletion docs/tut/crash-course/14-strict-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ want this.

Strict mode will run derived sources and effects twice each time they update.
This is to help ensure that derived source computations are pure, and that any
cleanups made in derived sources or effects are done correctly.
cleanups made in derived sources or effects are done properly.

```lua
local source = vide.source
Expand Down
56 changes: 28 additions & 28 deletions docs/tut/crash-course/15-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,52 +22,52 @@ Anything that happens in response to a source update.

Created with `effect()`.

## Reactive Scope
## Stable Scope

One of the two types of Vide scopes.

A scope created by certain functions such as:
Created by:

- `root()`
- `untrack()`
- `switch()`
- `indexes()`

Stable scopes do not track sources and never rerun.

New stable or reactive scopes can be created within a stable scope.

## Reactive Scope

Created by:

- `effect()`
- `derive()`

Reactive scopes can:
Reactive scopes do track sources and will rerun when those sources update.

- track sources that are read from within.
- rerun when a tracked source updates.
- track new reactive scopes created from within.
New reactive scopes cannot be created within a reactive scope, but stable scopes
can.

## Scope Owners

A reactive scope created within another reactive scope is *owned* by the other
reactive scope, with the exception of the reactive scope created by `root()`.
A scope created within another scope is *owned* by the other scope, with the
exception of the scope created by `root()`.

When a reactive scope is rerun or destroyed, all reactive scopes owned by it are
automatically destroyed.
When a scope is rerun or destroyed, all scopes owned by it are automatically
destroyed.

`root()`, which `mount()` uses internally, creates a reactive scope with no
owner, since it must be destroyed manually using a destructor
returned.
`root()` creates a stable scope with no owner, instead it is destroyed manually.

## Cleanup

Arbitrary code to run whenever a reactive scope is rerun or destroyed.
Arbitrary code to run whenever a stable or reactive scope is rerun or destroyed.

Queue a function to run using `cleanup()`.

## Tracking

Sources read from within a reactive scope will be tracked. This can be disabled
using `untrack()`, which will make reactive scopes temporarily ignore sources
read.

The reactive scope created by `root()` is non-tracking by default.

As a guard against misusage, a reactive scope cannot be created within a
reactive scope, unless it is made non-tracking using `untrack()`.

## Reactive Graph

The combination of reactive scopes can viewed graphically, called a
The combination of stable and reactive scopes can viewed graphically, called a
*reactive graph*. This can be a more intuitive way to think of the
relationships between effects and the sources they depend on.

Expand Down Expand Up @@ -114,10 +114,10 @@ count --> text
Notes:

- Since `count` is a source, not an effect, it can exist
outside of a root reactive scope.
outside of scopes.
- An update to `count` will cause `text` to rerun, which
then causes `effect` to rerun.
- When the root reactive scope is destroyed, `text` and
- When the root scope is destroyed, `text` and
`effect` will be destroyed alongside it, since they are
owned by it. `count` will be untouched and future updates
to `count` will have no effect.
7 changes: 0 additions & 7 deletions docs/tut/crash-course/2-creation.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,3 @@ return create "ScreenGui" {
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
to a string key.

::: warning
When creating an instance with no properties, it is important to not forget to
actually call the constructor: `create "Frame" {}` and not `create "Frame"`.
To be clear, `create "Frame"` returns a *function* which is a constructor for
that class, not an instance of that class.
:::
12 changes: 0 additions & 12 deletions docs/tut/crash-course/3-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,8 @@ local function App()
}
}
end

App().Parent = game.StarterGui
```

:::

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.

You can only modify the 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
values. This saves having to create and set every property each time. Also, when
updating the button component in future, any changes to the button file will be
seen anywhere the button is used in your app.
Loading

0 comments on commit fe31d8f

Please sign in to comment.