State variables
Last updated
Last updated
Application state is a fundamental concept in software engineering. "State variables" and "computed values" are useful features to encapsulate state in a single place, and to prevent duplication of code. Despite the common property of holding state values, there is a structural difference between these two entities:
State variables are initialized with an explicit value and can thereafter be explicitly mutated via a setter-function in JS actions. You can imagine them behaving similar to React state or redux. They are perfect for maintaining atomic pieces of state that are managed explicitly in user code.
Computed values are "named calculations" based on a JavaScript expression. They will reevaluate automatically in case any of their dependencies change (e.g. component properties, action results, or other state variables or computed values). They are perfect for encapsulating repetitive calculations in a single place with a guarantee that their values are always up to date.
As simplification, we are going to use the term "variable(s)" in this section to address both state variables and computed values.
State variables are supposed to hold serializable values. Use primitives, objects and arrays instead of maps, sets, dates, etc.
Both state variables and computed values are managed in the Explorer
tab of the left-hand panel of the app editor:
Each variable is listed with its name and the type of the value it currently holds. Clicking on a variable will expand the view to show details, like its current value. For complex values like arrays and objects, an inspector-view is rendered, which allows to selectively drill down into the object:
Clicking on the plus-icon next to the header of the variable list leads to the variable creation form. It requires you to provide a name as well as a JavaScript expression to define the initial value (state variable) or value computation (computed value) for the newly defined variable. Variable names must be unique across all state variables and computed values, however may overlap with components or actions. Use the "Save"-button to create the new variable and return to the list view.
Use the keyboard shortcut CMD+s (Mac) / CTRL+s (Win) to submit this form, either for creating or for editing variables.
The context menu of a variable can be accessed by clicking on the dotted icon that appears on hover next to the variable name. The "Edit" menu item leads to the same form that is used for creating a new variable. Both the name and the JavaScript expression for initial value / value computation can be modified. Modifying variable names will automatically update all usages of that variable in other code snippets (learn more).
Deleting a variable is possible through the same context menu like for entering edit-mode. To prevent accidental deletion of a variable that has usages in other code snippets, you will be warned when you try to delete such a variable.
Deleting a state variable or computed value cannot be undone. Deleting a variable with usages, despite the warning, will break all code snippets in which the deleted variable has been used (learn more).
State variables require to explicitly change their value in JS actions. For that, the state
variable is exposed in any place the user may use JavaScript (learn more). This state
object offers a getter property with the same name like the variable, as well as a setter function named by the pattern set{VarName}
, for example setCouponData
for a state variable called couponData
. The reactive behavior of code snippets in component properties covers state variables and computed values as well, i.e. property values will update automatically as soon as a variable value changes. The following example of a simple counter highlights these principles:
Computed values are created, edited and deleted in the same way like state variables. There are a couple of important differences though:
Computed values do not have an "Initial value" like state variables, but rather expect a JavaScript expression that is watched for changes.
The state
object does not offer a setter function, because the value will update automatically as soon as a dependency of its calculation expression changes
When defining the calculation expression, you have access to the same exposed variables that are also available in component properties or JS action code. Similar to component properties, you cannot access functions though, because the evaluation of this expression is not deterministic (learn more). Sticking with the example of a counter, we may use the counter
variable now in a computed value to derive new values from it:
State variables and computed values are exposed in JavaScript expressions through the state
object. This object provides all variable values as getter properties with the same name like the associated variable, and a setter function for each state variable.
In the previous example of a simple counter, we have been using two variables:
counter
: state variable holding the current numeric value of the counter, being incremented or decremented explicitly by clicking the buttons
counterSquared
: computed value, which calculates the squared value of the counter, by using the JavaScript expression state.counter * state.counter
.
The state
object will look as follows in this setup:
Our type system is not yet capable of inferring a better type than unknown
for the getters and setters. It is highly recommended to keep a consistent type in these variables to keep your application robust. We are working on better inference and hope this will be available soon!
JavaScript code in component properties and in the calculation expressions of other computed values will update automatically as soon as a state variable or computed value is updated (learn more).
Like with component properties, it is easy to create dependency cycles with computed values. The simplest scenario is two computed values that just reference each other in their calculation expressions. You will be notified with a warning in such case and it is highly recommended to resolve these dependency cycles immediately!