# Reactivity: Dynamic Scopes Dynamic scopes are scopes that are created or destroyed in response to source updates. Vide provides functions for some common use-cases for dynamic scopes. ## show() REACTIVE Shows a component if the source is truthy. Optionally shows a fallback component if the source is falsey. - **Type** ```luau function show(source: () -> unknown, component: Constructor): () -> T? function show(source: () -> unknown, component: Constructor, fallback: () -> U): () -> T | U type Constructor = () -> (T, number?) ``` - **Details** Creates a reactive scope internally to detect source updates. The component is run in a stable scope when truthy, otherwise the stable scope is destroyed. Returns a source holding an instance of the currently shown component or `nil` if no component is currently shown. Destruction of the scope can be delayed by returning the number of seconds to delay by, after the component. ## switch() REACTIVE Shows one of a set of components depending on a source and a mapping table. - **Type** ```luau function switch(source: () -> K): (map: Map>): () -> V? type Constructor = () -> (T, number?) ``` - **Details** Creates a reactive scope internally to detect source updates. When the source updates, its value is inputted into a map to get a component constructor. This component is then run in a stable scope. The previous stable scope is destroyed. Returns a source holding an instance of the currently shown component or `nil` if no component is currently shown. Destruction of the scope can be delayed by returning the number of seconds to delay by, after the component. - **Example** ```luau local logged = source(false) local button = switch(logged) { [true] = function() return Button { Text = "Log out", Toggle = logged } end, [false] = function() return Button { Text = "Log in", Toggle = logged } end } ``` ## indexes() REACTIVE Shows a component for each index in a table. - **Type** ```luau function indexes( source: () -> Map, constructor: (value: () -> VI, index: KI) -> (VO, number?) ): Array ``` - **Details** Creates a reactive scope internally to detect source updates. When the source table updates, a component is generated for each index in the table. - For any added index, the `constructor` function is run in a new stable scope to produce an instance that is cached. - For any removed index, the stable scope for that index is destroyed. The `constructor` function is called with: 1. A *source containing the index's value*. 2. The *index itself*. Anytime an existing index's value changes, the `constructor` function is not rerun, instead, that index's corresponding source is updated with the new value. Returns a source holding an array of instances currently shown. Destruction of the scope can be delayed by returning the number of seconds to delay by, after the component. - **Example** ```luau type Item = { name: string, icon: number } local items = source {} :: () -> Array local displays = indexes(items, function(item, i) return ItemDisplay { Name = function() return i .. ": " .. item().name end, Image = function() return "rbxassetid://" .. item().icon end, } end) ``` ## values() REACTIVE Shows a component for each value in a table. - **Type** ```luau function values( source: () -> Map, constructor: (value: VI, index: () -> KI) -> (VO, number?) ): Array - **Details** Operates with the same idea as `indexes()`, but applied to values instead of indexes. Creates a reactive scope internally to detect source updates. When the source table updates, a component is generated for each value in the table. - For any added value, the `constructor` function is run in a new stable scope to produce an instance that is cached. - For any removed value, the stable scope for that value is destroyed. The `constructor` function is called with: 1. The *value itself*. 2. A *source containing the value's index*. Anytime an existing value's index changes, the `constructor` function is not rerun, instead, that value's corresponding source is updated with the new index. Returns a source holding an array of instances currently shown. Destruction of the scope can be delayed by returning the number of seconds to delay by, after the component. ::: warning Having the same values appear multiple times in the input source table can cause unexpected behavior. Strict mode has checks for this. ::: - **Example** ```luau type Item = { name: string, icon: number } local items = source {} :: () -> Array local displays = values(items, function(item, i) return ItemDisplay { Name = function() return i() .. ": " .. item.Name end Image = "rbxassetid://" .. item.icon, } end) ``` - **Extra** When should you use `indexes()` and `values()`? `values()` should be used when you have a fixed set of objects where the same objects can be re-arranged in the source table. It maps a value to a UI element. e.g. - List of all players. - Inventory of items. - Chat message history. - Toast notifications. `indexes()` should be used in other cases, especially when your source table has primitive values. It maps an index to a UI element. e.g. - List of character or weapon stats. In most cases, both functions will produce the same observed result. The main difference is performance, picking the right function to use can result in less property updates and less re-renders. One case to note is that `values()` works nicely when animating re-ordering of instances, since the source index can be used to animate a change in position for the UI element. --------------------------------------------------------------------------------