From 6975938fb97a23bcae3e76682060969e20798321 Mon Sep 17 00:00:00 2001 From: Gregory Wolanski Date: Sat, 25 Jul 2020 14:31:50 +0200 Subject: [PATCH] Fix typos --- _glossary/atomic-state.md | 6 +- _glossary/automatic-transition.md | 15 +- _glossary/compound-state.md | 39 ++-- _glossary/condition-state.md | 58 +++--- _glossary/delayed-event.md | 25 ++- _glossary/delayed-transition.md | 6 +- _glossary/enter.md | 4 +- _glossary/final-state.md | 13 +- _glossary/initial-state.md | 31 ++- _glossary/internal-event.md | 31 ++- _glossary/parallel-state.md | 32 ++-- _glossary/pseudostate.md | 5 +- _glossary/raised-event.md | 10 +- _glossary/refine.md | 6 +- _glossary/self-transition.md | 12 +- _glossary/state.md | 41 ++-- _glossary/transition.md | 25 ++- how-to-use-statecharts.md | 150 +++++++-------- use-case-statecharts-in-user-interfaces.md | 209 ++++++++++----------- 19 files changed, 340 insertions(+), 378 deletions(-) diff --git a/_glossary/atomic-state.md b/_glossary/atomic-state.md index 5019b72..a4be631 100644 --- a/_glossary/atomic-state.md +++ b/_glossary/atomic-state.md @@ -1,6 +1,6 @@ --- title: Atomic state -oneliner: a state that has no substates +oneliner: A state that has no substates keywords: atomic, simple, empty state breadcrumbs: - id: state @@ -15,7 +15,7 @@ aka: _Also known as **Simple state**_ -An atomic state is a [state](state.html){:.glossary} that has no _substates_. States that have substates are either [compound states](compound-state.html){:.glossary} or [parallel states](parallel-state.html){:.glossary}. +An atomic state is a [state](state.html){:.glossary} that has no _substates_. States that have substates are either [compound states](compound-state.html){:.glossary} or [parallel states](parallel-state.html){:.glossary}. Atomic states do not define "initial" states either. @@ -29,7 +29,7 @@ For atomic states that have no actions, and are otherwise empty, a very small, r In SCXML, an atomic state is any state that has no _state_ children: -``` xml +```xml diff --git a/_glossary/automatic-transition.md b/_glossary/automatic-transition.md index 9b0f93e..3a3f792 100644 --- a/_glossary/automatic-transition.md +++ b/_glossary/automatic-transition.md @@ -1,6 +1,6 @@ --- title: Automatic transition -oneliner: a transition that happens immediately upon entering a state or immediately after a condition arises +oneliner: A transition that happens immediately upon entering a state or immediately after a condition arises breadcrumbs: - id: transition name: Transition @@ -18,9 +18,9 @@ Automatic transitions are [transitions](transition.html){:.glossary} that are tr Automatic transitions don't have an associated [event](event.html){:.glossary}, as the mere being in the state implies that the transition should be taken. -Automatic transitions are usually [guarded](guard.html){:.glossary}. Such a guarded automatic transition is checked immediately after the state is entered. If the condition doesn't hold then the machine remains in the state, with this automatic transition in play _for as long as the state is active_. Every time the statechart handles an event, the guard condition for these automatic transitions are checked. If ever the guard condition ever succeeds, then the transition happens. +Automatic transitions are usually [guarded](guard.html){:.glossary}. Such a guarded automatic transition is checked immediately after the state is entered. If the condition doesn't hold then the machine remains in the state, with this automatic transition in play _for as long as the state is active_. Every time the statechart handles an event, the guard condition for these automatic transitions are checked. If ever the guard condition ever succeeds, then the transition happens. -If there are many automatic transitions in play, they are all checked. In some statechart systems, only one guard is allowed to be true at any point in time; in others, the transitions are ordered, and the guards are checked until a one of them succeeds. +If there are many automatic transitions in play, they are all checked. In some statechart systems, only one guard is allowed to be true at any point in time; in others, the transitions are ordered, and the guards are checked until a one of them succeeds. ## Notation @@ -31,16 +31,15 @@ A transition arrow, but without the name of an event, only guards and/or actions ----------------------> ``` - ## Usage -Guarded transitions can be used to cause a machine to "wait" in a certain state, _until_ some condition holds, regardless of what else is happening in the form of events. For example, a machine could be in the 'filling' state until some threshold is reached, by defining an automatic transition from the 'filling' state with a guard `contents >= capacity`. Immediately after the contents reach the capacity, it would exit from this 'filling' state. +Guarded transitions can be used to cause a machine to "wait" in a certain state, _until_ some condition holds, regardless of what else is happening in the form of events. For example, a machine could be in the 'filling' state until some threshold is reached, by defining an automatic transition from the 'filling' state with a guard `contents >= capacity`. Immediately after the contents reach the capacity, it would exit from this 'filling' state. -By using an _in_ guard, it is possible to coordinate different parts of a [parallel state](parallel-state.html){:.glossary}. When the one region ends up on a certain state, it can wait until another region enters a specific state. +By using an _in_ guard, it is possible to coordinate different parts of a [parallel state](parallel-state.html){:.glossary}. When the one region ends up on a certain state, it can wait until another region enters a specific state. -If a machine is in a state with a guarded automatic transition, then that guard is checked as often as possible. Being event driven, the guards are effectively only checked whenever an event has been processed, but also after other automatic transitions have fired, or other internal events (such as [raised events](raised-event.html){:.glossary} are fired. +If a machine is in a state with a guarded automatic transition, then that guard is checked as often as possible. Being event–driven, the guards are effectively only checked whenever an event has been processed, but also after other automatic transitions have fired, or other internal events (such as [raised events](raised-event.html){:.glossary}) are fired. -Automatic transitions can be used to implement a [condition states](condition-state.html){:.glossary}, in other words, a state that only has automatic transitions. This is done by creating a state that only declares guarded automatic transitions, in such a way that it is guaranteed that the machine will always pick a transition upon entry, never _resting_ in that state. +Automatic transitions can be used to implement a [condition states](condition-state.html){:.glossary}, in other words, a state that only has automatic transitions. This is done by creating a state that only declares guarded automatic transitions, in such a way that it is guaranteed that the machine will always pick a transition upon entry, never _resting_ in that state. ## SCXML diff --git a/_glossary/compound-state.md b/_glossary/compound-state.md index b64991b..30043f0 100644 --- a/_glossary/compound-state.md +++ b/_glossary/compound-state.md @@ -5,7 +5,7 @@ primaryImage: compound-state.svg keywords: - compound - compound state - - substate + - substate - composite - composite state - child state @@ -28,7 +28,7 @@ aka: _Also known as **Composite state** and **Or state**_ -A compound state is a [state](state.html){:.glossary} that includes one or more substates. It is the main differentiating factor between traditional state machines and statecharts. A compound state can be thought of as _containing its own state machine_. +A compound state is a [state](state.html){:.glossary} that includes one or more substates. It is the main differentiating factor between traditional state machines and statecharts. A compound state can be thought of as _containing its own state machine_. ## Notation @@ -36,51 +36,50 @@ A compound state is a normal state with its substates depicted _inside_ the bord ![A state "Off" with substates A and B](compound-state.svg) -Here, the state called **Off** is a compound state. It has two states **A** and **B** as its substates. Note how the substates constitute their own state machine, even with an [initial state](initial-state.html){:.glossary}. +Here, the state called **Off** is a compound state. It has two states **A** and **B** as its substates. Note how the substates constitute their own state machine, even with an [initial state](initial-state.html){:.glossary}. ## Description If there are more than one substates, one of them is usually designated as the [initial](initial-state.html){:.glossary} state of that compound state. -When a compound state is active, its substates behave as though they were an active state machine: Exactly one child state must also be active. This means that: +When a compound state is active, its substates behave as though they were an active state machine: Exactly one child state must also be active. This means that: -* When a compound state is [entered](enter.html){:.glossary}, it must also enter exactly one of its substates, usually its initial state. -* When an [event](event.html){:.glossary} happens, the _substates_ have priority when it comes to selecting which transition to follow. If a substate happens to handles an event, the event is consumed, it isn't passed to the parent compound state. -* When a substate [transitions](transition.html){:.glossary} to another substate, both "inside" the compound state, the compound state does _not_ exit or enter; it remains active. -* When a compound state [exits](exit.html){:.glossary}, its substate is simultaneously exited too. (Ttechnically, the substate exits first, _then_ its parent.) +- When a compound state is [entered](enter.html){:.glossary}, it must also enter exactly one of its substates, usually its initial state. +- When an [event](event.html){:.glossary} happens, the _substates_ have priority when it comes to selecting which transition to follow. If a substate happens to handles an event, the event is consumed, it isn't passed to the parent compound state. +- When a substate [transitions](transition.html){:.glossary} to another substate, both "inside" the compound state, the compound state does _not_ exit or enter; it remains active. +- When a compound state [exits](exit.html){:.glossary}, its substate is simultaneously exited too. (Technically, the substate exits first, _then_ its parent.) Compound states may be nested, or include [parallel](parallel-state.html){:.glossary} states. The opposite of a compound state is an [atomic state](atomic-state.html){:.glossary}, which is a state with no substates. -A compound state is allowed to define transitions to its child states. Normally, when a transition leads from a state, it causes that state to be exited. For transitions from a compound state to one of its descendantes, it is possible to define a transition that avoids exiting and entering the compound state itself, such transitions are called [local transitions](local-transition.html){:.glossary}. +A compound state is allowed to define transitions to its child states. Normally, when a transition leads from a state, it causes that state to be exited. For transitions from a compound state to one of its descendantes, it is possible to define a transition that avoids exiting and entering the compound state itself, such transitions are called [local transitions](local-transition.html){:.glossary}. ## Usage -An atomic state is often converted to a compound state in order to change its behaviour slightly. The newly introduced substates get a chance to override the behaviour by defining how to react to different events. The substates only need to define the differences in behaviour. The (now) compound state defines the general behaviour, and substates define deviations from this behaviour. +An atomic state is often converted to a compound state in order to change its behaviour slightly. The newly introduced substates get a chance to override the behaviour by defining how to react to different events. The substates only need to define the differences in behaviour. The (now) compound state defines the general behaviour, and substates define deviations from this behaviour. -The act of changing an atomic state into a compound state (by introducing a substate or two) is called _refining_ the state. The refinement alludes to the different behaviour encoded in the substates as being a _refinement_, or _specialization_ of the general behavour of the compound state. +The act of changing an atomic state into a compound state (by introducing a substate or two) is called _refining_ the state. The refinement alludes to the different behaviour encoded in the substates as being a _refinement_, or _specialization_ of the general behavour of the compound state. -A group of states can be collected into a compound state to factor out _common transitions_. For example, if five sibling states all have the same transitions to a particular target, it can be beneficial to move those five states into a single compound state, and then move the individual transitions to the compound state. +A group of states can be collected into a compound state to factor out _common transitions_. For example, if five sibling states all have the same transitions to a particular target, it can be beneficial to move those five states into a single compound state, and then move the individual transitions to the compound state. The act of grouping states with commonalities into a compound state is called _clustering_. -Technically a statechart itself is usually (at the top level) a compound state itself. Some systems allow the machine to be a parallel state. - +Technically a statechart itself is usually (at the top level) a compound state itself. Some systems allow the machine to be a parallel state. ### Zooming in and out -The nesting of states in a hierarchy can lead to complicated charts when it is visualized. It is possible to conceal the internals of a compound state by excluding substates from the visualization. This technique is called _zooming out_. Zooming _in_ would then reveal the details. +The nesting of states in a hierarchy can lead to complicated charts when it is visualized. It is possible to conceal the internals of a compound state by excluding substates from the visualization. This technique is called _zooming out_. Zooming _in_ would then reveal the details. ## SCXML In Statechart XML, a compound state is any state with nested state elements as direct children; this includes ``, `` elements too, as these are also state elements. -``` xml +```xml - - + + ``` @@ -88,7 +87,7 @@ A compound state's initial state is either identified by the `initial` attribute ## XState -An XState [compound state](https://xstate.js.org/docs/guides/hierarchical.html) is declared using the `states` property of the state, holding an object containing substates. Each key value pair declares the name and definition of the state, respectively. +An XState [compound state](https://xstate.js.org/docs/guides/hierarchical.html) is declared using the `states` property of the state, holding an object containing substates. Each key value pair declares the name and definition of the state, respectively. ```javascript "off": { @@ -109,7 +108,7 @@ The `initial` property must specify the initial state. In SCION-CORE, a compound state is declared by specifying the `states` property of the state in question, containing an array of state objects. -``` javascript +```javascript { id: "off", states: [ diff --git a/_glossary/condition-state.md b/_glossary/condition-state.md index 7133aab..daa4a41 100644 --- a/_glossary/condition-state.md +++ b/_glossary/condition-state.md @@ -1,6 +1,6 @@ --- title: Condition state -oneliner: A pseudo-state that only has transitions, guarded in such a way that it immediately moves to another state. +oneliner: A pseudo-state that only has transitions, guarded in such a way that it immediately moves to another state date: 2019-02-24 breadcrumbs: - id: state @@ -18,9 +18,9 @@ aka: # Condition state -A condition state is a state that essentially consists _solely_ of [automatic](automatic-transition.html){:.glossary}, [guarded](guard.html){:.glossary} [transitions](transition.html){:.glossary}, so that upon entry, the state will always immediately exit to another state. A condition state is used to group similar sets of incoming transitions, so that the guards of those transitions don't need to be repeated. It can also be used to simplify entry to a state, by allowing the source of a transition to target a single condition state, where the choice of target state happens. +A condition state is a state that essentially consists _solely_ of [automatic](automatic-transition.html){:.glossary}, [guarded](guard.html){:.glossary} [transitions](transition.html){:.glossary}, so that upon entry, the state will always immediately exit to another state. A condition state is used to group similar sets of incoming transitions, so that the guards of those transitions don't need to be repeated. It can also be used to simplify entry to a state, by allowing the source of a transition to target a single condition state, where the choice of target state happens. -To be called a condition state, there should be no actions or activities defined in the state. Condition states do not have child states. All transitions must be automatic, and care must be taken when defining the guards, to ensure that at least one transition will always be taken. This is usually done by including one unguarded automatic transition. +To be called a condition state, there should be no actions or activities defined in the state. Condition states do not have child states. All transitions must be automatic, and care must be taken when defining the guards, to ensure that at least one transition will always be taken. This is usually done by including one unguarded automatic transition. ## Notation @@ -28,18 +28,18 @@ Harel's original paper defined a condition state to be denoted using a capital l ![A compound state B with condition state](condition-state.svg) -Note that in UML, _junction states_ and _choice states_ are similar to condition states, and use slightly different notation: Junction states are denoted by an opaque, black circle, while choice states are denoted by a diamond. +Note that in UML, _junction states_ and _choice states_ are similar to condition states, and use slightly different notation: Junction states are denoted by an opaque, black circle, while choice states are denoted by a diamond. ## Usage -Statecharts can easily get cluttered when the number of states increases. Not necessarily due to the number of states, but rather the number of transitions between the states. Condition states allow you to combine similar transitions thereby reducing the number of lines connecting the various states. The usefulness is increased if many of the transitions have the same guard conditions. +Statecharts can easily get cluttered when the number of states increases. Not necessarily due to the number of states, but rather the number of transitions between the states. Condition states allow you to combine similar transitions thereby reducing the number of lines connecting the various states. The usefulness is increased if many of the transitions have the same guard conditions. Condition states are often used in two ways: -* Reduce the number of transitions from a compound state that end up in the same target, by combining the transitions in the source state. -* Combine several guarded transitions going in to a compound state (that come from the same source). This is known as a conditional entry to a state. +- Reduce the number of transitions from a compound state that end up in the same target, by combining the transitions in the source state. +- Combine several guarded transitions going in to a compound state (that come from the same source). This is known as a conditional entry to a state. -When making changes to a statechart, it is often the case that you need similar sets of guards to control different states. In the following example, we have two main states A and B, where state A has three separte transitions for event _e_, each leading to a specific substate of B. The choice of which substate we end up in is determined by the individual guards. +When making changes to a statechart, it is often the case that you need similar sets of guards to control different states. In the following example, we have two main states A and B, where state A has three separte transitions for event _e_, each leading to a specific substate of B. The choice of which substate we end up in is determined by the individual guards. **The guards that determine the final source, are outside the compound state B.**{:.caption} ![Atomic state A and compound state B, the latter with substates 1, 2, and 3. An event e enters 1, 2 or 3 depending on guards](condition-state-before.svg) @@ -49,24 +49,25 @@ The problem is apparent if you try to introduce another state, say C, which also **By adding a single state C, which has transitions similar to A, we had to duplicate many guards.**{:.caption} ![Atomic states A and C, and compound state B, the latter with substates 1, 2, and 3. An event e in A (or event f in C) enters states 1, 2 or 3 depending on guards](condition-state-before-two.svg) -By defining a condition state inside the compound state itself, the guard definitions are closer to the target states, making the statechart easier to reason about. Let's remove state C again, and instead first introduce the _condition state_: +By defining a condition state inside the compound state itself, the guard definitions are closer to the target states, making the statechart easier to reason about. Let's remove state C again, and instead first introduce the _condition state_: -**The guards that determine the correct substate of B are now inside B, and state A is already easier to understand. It also becomes easier to make changes to the statechart.**{:.caption} +**The guards that determine the correct substate of B are now inside B, and state A is already easier to understand. It also becomes easier to make changes to the statechart.**{:.caption} ![Atomic state A, and compound state B, the latter with substates 1, 2, and 3. An event e in A always leads to a condition state, which determines the final state using guards](condition-state-after.svg) -Now, it becomes a lot easier to introduce the C state, without doubling the amount of transitions. Now, since we have a state that represents the _conditional entry_, we can re-use that condition state by transitioning to it: +Now, it becomes a lot easier to introduce the C state, without doubling the amount of transitions. Now, since we have a state that represents the _conditional entry_, we can re-use that condition state by transitioning to it: **The guards that determine the state of B are inside B, and states A and C are easier to understand.**{:.caption} ![Atomic states A and C, and compound state B, the latter with substates 1, 2, and 3. An event e in A (or event f in C) enters a condition state, which determines the final state using guards](condition-state-after-two.svg) -Condition states can help reduce clutter when introducing states and a whole set of transitions need to be copied over to a new state, or when several different events need similar sets of guards. In such situations, consider introducing a condition state to encapsulate the logic. Condition states can also help move guards closer to the target states, where this is desirable. +Condition states can help reduce clutter when introducing states and a whole set of transitions need to be copied over to a new state, or when several different events need similar sets of guards. In such situations, consider introducing a condition state to encapsulate the logic. Condition states can also help move guards closer to the target states, where this is desirable. ## SCXML Condition states are not native to SCXML, but can easily be implemented by making a `` element that consists only of automatic guarded transitions, ensuring that being in the state has no other effects than transitioning to another state: **`B_COND` is a condition state, used to provide a conditional entry to state B.**{:.caption} -``` xml + +```xml @@ -83,24 +84,25 @@ Condition states are not native to SCXML, but can easily be implemented by makin ``` -Here we have a state A which, on event "e" will transition to B_COND, which is a condition state, because it consists only of automatic, guarded transitions. When in state A, and the event "e" happens: +Here we have a state A which, on event "e" will transition to B_COND, which is a condition state, because it consists only of automatic, guarded transitions. When in state A, and the event "e" happens: -* A will exit and enter B_COND. -* Being in B_COND will immediately evaluate the guard `a == 1` and go directly to B1 if this is true. -* Otherwise, B_COND will evaluate the guard `a == 0` and go to B2 if this is true. -* Otherwiser, B_COND will go to B3. +- A will exit and enter B_COND. +- Being in B_COND will immediately evaluate the guard `a == 1` and go directly to B1 if this is true. +- Otherwise, B_COND will evaluate the guard `a == 0` and go to B2 if this is true. +- Otherwiser, B_COND will go to B3. This offers many benefits: -* If this were to be implemented without the condition states, the transitions with `cond` logic would be in state `A`, whereas it is probably wiser to keep this logic closer to the states B1, B2 and B3. -* If more events (e.g. ``), or more sources of events (e.g. other than from `A`) lead to the same guarded transitions, a condition state reduces the amount of transitions that need to be defined. +- If this were to be implemented without the condition states, the transitions with `cond` logic would be in state `A`, whereas it is probably wiser to keep this logic closer to the states B1, B2 and B3. +- If more events (e.g. ``), or more sources of events (e.g. other than from `A`) lead to the same guarded transitions, a condition state reduces the amount of transitions that need to be defined. ## XState Condition states are not native to XState, but can easily be implemented by making a state that consists only of automatic, guarded transitions, ensuring that being in the state has no other effects than transitioning to another state. **`COND` is a condition state, used to provide a conditional entry to state B.**{:.caption} -``` javascript + +```javascript { states: { A: { @@ -129,17 +131,17 @@ Condition states are not native to XState, but can easily be implemented by maki } ``` -Here we have a state "A" which, on event "e" will transition to "B.COND", which is a condition state, because it consists only of automatic, guarded transitions. When in state A, and the event "e" happens: +Here we have a state "A" which, on event "e" will transition to "B.COND", which is a condition state, because it consists only of automatic, guarded transitions. When in state A, and the event "e" happens: -* A will exit and enter B.COND. -* Being in COND will immediately evaluate the guard `a == 1` and go directly to "1" if this is true. -* Otherwise, COND will evaluate the guard `a == 0` and go to "2" if this is true. -* Otherwiser, COND will go to "3". +- A will exit and enter B.COND. +- Being in COND will immediately evaluate the guard `a == 1` and go directly to "1" if this is true. +- Otherwise, COND will evaluate the guard `a == 0` and go to "2" if this is true. +- Otherwiser, COND will go to "3". This offers many benefits: -* If this were to be implemented without the condition states, the transitions with `cond` logic would be in state `A`, whereas it is probably wiser to keep this logic closer to the states B.1, B.2 and B.3. -* If more events (e.g. `on: {e2: "B.COND"}`), or more sources of events elsewhere in the statechart (e.g. other than from `A`) lead to the same choices being made, a choice state reduces the amount of transitions that need to be defined. +- If this were to be implemented without the condition states, the transitions with `cond` logic would be in state `A`, whereas it is probably wiser to keep this logic closer to the states B.1, B.2 and B.3. +- If more events (e.g. `on: {e2: "B.COND"}`), or more sources of events elsewhere in the statechart (e.g. other than from `A`) lead to the same choices being made, a choice state reduces the amount of transitions that need to be defined. ## Relation to UML choice and junction states diff --git a/_glossary/delayed-event.md b/_glossary/delayed-event.md index 97711ea..b3a755b 100644 --- a/_glossary/delayed-event.md +++ b/_glossary/delayed-event.md @@ -1,6 +1,6 @@ --- title: Delayed event -oneliner: an event that is raised some time after a transition takes place. +oneliner: An event that is raised some time after a transition takes place breadcrumbs: - id: event name: Event @@ -8,7 +8,7 @@ breadcrumbs: # Delayed Event -A delayed event is the ability for the statechart to send an event after a certain period of time. Delayed events can be used to simulate [delayed transitions](delayed-transition.html){:.glossary}. +A delayed event is the ability for the statechart to send an event after a certain period of time. Delayed events can be used to simulate [delayed transitions](delayed-transition.html){:.glossary}. ## Notation @@ -19,45 +19,44 @@ Not sure A delayed event is specified using the following syntax: ```xml - + ``` -The `delay` attribute specifies a CSS timing value. The statechart implementation will raise the specified event after the amount of time. It is possible to _cancel_ such a delayed event. In order to do so, an identifier needs to be added to the `` +The `delay` attribute specifies a CSS timing value. The statechart implementation will raise the specified event after the amount of time. It is possible to _cancel_ such a delayed event. In order to do so, an identifier needs to be added to the `` ```xml - + ``` To cancel, use the identifier in a `` element: ```xml - + ``` These operations are typically done in entry or exit handlers, but can also be used in transitions. ### Simulating delayed transitions -Delayed transitions are not part of Statechart XML. Instead, you have to specify to send a delayed event, and a normal transition that handles that event. +Delayed transitions are not part of Statechart XML. Instead, you have to specify to send a delayed event, and a normal transition that handles that event. For a simple delayed transition from `some_state` to `some_other_state` after 5 seconds, you need: -* An entry handler that send a "fake" event `my_event` with a 5 second delay -* An exit handler that cancels the sending of the event (in case the state is exited before 5 seconds) -* A transition that handles the "fake" event by transitioning to the `some_other_state`. +- An entry handler that send a "fake" event `my_event` with a 5 second delay +- An exit handler that cancels the sending of the event (in case the state is exited before 5 seconds) +- A transition that handles the "fake" event by transitioning to the `some_other_state`. Care must be taken to ensure statechart-wide unique values for the event and id, because there is no "local" scope. This is the result: - ```xml - + - + diff --git a/_glossary/delayed-transition.md b/_glossary/delayed-transition.md index 26689a0..0f2828d 100644 --- a/_glossary/delayed-transition.md +++ b/_glossary/delayed-transition.md @@ -1,6 +1,6 @@ --- title: Delayed transition -oneliner: a transition that executes automatically when a machine has been in a state for a particular amount of time +oneliner: A transition that executes automatically when a machine has been in a state for a particular amount of time breadcrumbs: - id: transition name: Transition @@ -10,7 +10,7 @@ breadcrumbs: A delayed transition is an [transition](transition.html){:.glossary} that happens after a period of time, specifically being in a specific state for a certain amount of time. -Whenever such a delayed transition exists in a state, the state machine will execute the transition if and only if the machine has stayed continously in the state for a given period of time. If the state has a [self transition](self-transition.html){:.glossary} which is executed, this causes the state to be [exited](exit.html){:.glossary} and then [entered](enter.html){:.glossary} again, interrupting any continuity. +Whenever such a delayed transition exists in a state, the state machine will execute the transition if and only if the machine has stayed continously in the state for a given period of time. If the state has a [self transition](self-transition.html){:.glossary} which is executed, this causes the state to be [exited](exit.html){:.glossary} and then [entered](enter.html){:.glossary} again, interrupting any continuity. ## Notation @@ -21,7 +21,7 @@ A delayed event uses the phrase "after " as the name of the event ## SCXML -Delayed transitions are not part of Statechart XML. Instead, you have to specify to send a [delayed event](delayed-event.html){:.glossary}, and a normal transition that handles that event. The delayed event must be given a name, and also a unique identifier (in order to cancel the delayed event). +Delayed transitions are not part of Statechart XML. Instead, you have to specify to send a [delayed event](delayed-event.html){:.glossary}, and a normal transition that handles that event. The delayed event must be given a name, and also a unique identifier (in order to cancel the delayed event). ## XState diff --git a/_glossary/enter.md b/_glossary/enter.md index 21f9de4..c7b3a65 100644 --- a/_glossary/enter.md +++ b/_glossary/enter.md @@ -7,10 +7,10 @@ redirect_from: # Enter -Refers to the act of _entering a state_ during the execution of a state machine. The state machine keeps track of which state is [active](active.html){:.glossary} and its behaviour is defined based on the active state(s). A state may declare entry [actions](action.html){:.glossary}, which will be executed when a state is entered. +Refers to the act of _entering a state_ during the execution of a state machine. The state machine keeps track of which state is [active](active.html){:.glossary} and its behaviour is defined based on the active state(s). A state may declare entry [actions](action.html){:.glossary}, which will be executed when a state is entered. If a state is a [compound state](compound-state.html){:.glossary}, the defined [initial state](initial-state.html){:.glossary} is also entered, since in an active compound state, exactly one substate must also be active. If a state is a [parallel state], each region and their initial states will also be entered. -Entry of a state is defined to be instantaneous for all practical purposes; this includes the execution of any entry actions, and the entry of all substates. In other words, a state can never be "half entered" or "half active". +Entry of a state is defined to be instantaneous for all practical purposes; this includes the execution of any entry actions, and the entry of all substates. In other words, a state can never be "half entered" or "half active." diff --git a/_glossary/final-state.md b/_glossary/final-state.md index 8a5eac7..cf8965b 100644 --- a/_glossary/final-state.md +++ b/_glossary/final-state.md @@ -1,6 +1,6 @@ --- title: Final state -oneliner: a helper state which designates that its parent state has completed +oneliner: A helper state which designates that its parent state has completed breadcrumbs: - id: state name: State @@ -8,18 +8,17 @@ breadcrumbs: # Final State -A final state is a state in a [compound state](compound-state.html){:.glossary} that designates that the compound state in question has completed, i.e. will not process any further events. Reaching a final state can generate an internal event, which in turn can allow other parts of the state machine to react to the fact that the compound state has "completed". +A final state is a state in a [compound state](compound-state.html){:.glossary} that designates that the compound state in question has completed, i.e. will not process any further events. Reaching a final state can generate an internal event, which in turn can allow other parts of the state machine to react to the fact that the compound state has "completed". -The "completion" of a state is wholly defined by the statechart itself. +The "completion" of a state is wholly defined by the statechart itself. ## Notation -Final states are depicted using a solid filled circle, like the [initial state](initial-state.html){:.glossary} with an additional circle enclosing it. Transitions from the final state must be [automatic], in other words they can not rely on events. - +Final states are depicted using a solid filled circle, like the [initial state](initial-state.html){:.glossary} with an additional circle enclosing it. Transitions from the final state must be [automatic], in other words they can not rely on events. ## SCXML -In SCXML, the `` element is used to define a final state. When a final state is reached, the parent state generates a `done.state.` (where `` is the state of the parent state). This allows the statechart to react to those events, effectively mimicing transitions upon the "completion" of states. +In SCXML, the `` element is used to define a final state. When a final state is reached, the parent state generates a `done.state.` (where `` is the state of the parent state). This allows the statechart to react to those events, effectively mimicing transitions upon the "completion" of states. ## XState @@ -27,7 +26,7 @@ In XState, final states are defined with `type: "final"`: ```js resolved: { - type: 'final'; + type: 'final' } ``` diff --git a/_glossary/initial-state.md b/_glossary/initial-state.md index 398d2ad..da1cf81 100644 --- a/_glossary/initial-state.md +++ b/_glossary/initial-state.md @@ -1,6 +1,6 @@ --- title: Initial state -oneliner: The state which is entered when a state machine starts, or when a compound state is entered. +oneliner: The state which is entered when a state machine starts, or when a compound state is entered primaryImage: initial-state.svg keywords: - default @@ -21,45 +21,45 @@ aka: url: initial-state.html --- -When a state machine starts, it starts by [entering](enter.html){:.glossary} the machine's **initial state**. Likewise, when a [compound state](compound-state.html){:.glossary} is entered, _its_ **initial state** is also entered. The initial state is not a separate state, but more like an indication of which state that the machine should _start_ in by default. +When a state machine starts, it starts by [entering](enter.html){:.glossary} the machine's **initial state**. Likewise, when a [compound state](compound-state.html){:.glossary} is entered, _its_ **initial state** is also entered. The initial state is not a separate state, but more like an indication of which state that the machine should _start_ in by default. -A [transition](transition.html){:.glossary} from a different state can target a substate of a compound state, thereby bypassing the state's initial state. An initial state should be thought of as the default starting point of a compound state if a transition points directly to the compound state. +A [transition](transition.html){:.glossary} from a different state can target a substate of a compound state, thereby bypassing the state's initial state. An initial state should be thought of as the default starting point of a compound state if a transition points directly to the compound state. ## Notation -A state machine, or compound state's _initial_ state is specified by way a black circle with exactly one transition arrow to a sibling state that should be entered. The black circle and arrow have no name, it is the target state that is called the 'initial' state. +A state machine, or compound state's _initial_ state is specified by way a black circle with exactly one transition arrow to a sibling state that should be entered. The black circle and arrow have no name, it is the target state that is called the "initial" state. ![Black circle pointing to a state labeled A. A is the initial state.](initial-state.svg) ## Usage -Initial states are impossible to avoid, and follows from the definition of compound states: For a compound state, whenever it becomes active, exactly one of its direct children are also active. The "initial" state simply denotes _which_ of the child states become active when this happens. +Initial states are impossible to avoid, and follows from the definition of compound states: For a compound state, whenever it becomes active, exactly one of its direct children are also active. The "initial" state simply denotes _which_ of the child states become active when this happens. -Picking the right initial state can help decouple the various parts of the statechart. It allows transitions to target the compound (parent) state instead of a child directly. This allows collapsing the view of a compound state to see the bigger picture. +Picking the right initial state can help decouple the various parts of the statechart. It allows transitions to target the compound (parent) state instead of a child directly. This allows collapsing the view of a compound state to see the bigger picture. ## SCXML In Statechart XML, the initial state is denoted in one of these ways: -* using the `initial` attribute of the `` element: +- Using the `initial` attribute of the `` element: ```xml - - + + ``` -* Using the `` element instead of a `` element. The `` element is identical to any other `` except that it is an `` state. +- Using the `` element instead of a `` element. The `` element is identical to any other `` except that it is an `` state. ```xml - - + + ``` -* Implicitly, by relying on document order, the first `` element will be the initial state: +- Implicitly, by relying on document order, the first `` element will be the initial state: ```xml - - + + ``` @@ -77,4 +77,3 @@ In XState, a compound state must have the `initial` property declaring the key o } } ``` - diff --git a/_glossary/internal-event.md b/_glossary/internal-event.md index 88f1cf1..bef51cf 100644 --- a/_glossary/internal-event.md +++ b/_glossary/internal-event.md @@ -15,14 +15,13 @@ An internal event is a [generated event](generated-event.html){:.glossary} that ## Notation -Internal events don't have specific names; the events themselves are generated automatically by the statechart. However, sensing (reacting to) an event is done by way of transitions that specify events such as `entered(somestate)` or `in(somestate)`. +Internal events don't have specific names; the events themselves are generated automatically by the statechart. However, sensing (reacting to) an event is done by way of transitions that specify events such as `entered(somestate)` or `in(somestate)`. ``` in (somestate) ———————————————————————> ``` - ``` entered (B) ———————————————————————> @@ -30,37 +29,35 @@ Internal events don't have specific names; the events themselves are generated a ## Usage -When processing an external event, the statechart might go from state A to state B; this state change can "generate" new events such as +When processing an external event, the statechart might go from state A to state B; this state change can "generate" new events such as: -* We're no longer 'in' state A -* State A was exited -* State B was entered -* We're now 'in' state B +- We're no longer 'in' state A +- State A was exited +- State B was entered +- We're now 'in' state B -Immediately after a state transition happens (and importantly before the next external event is processed), the state machine checks if any transitions get "enabled" by any such internal events. If there are any, then they are considered _triggered_ by an internal event, and those transitions happen immediately. +Immediately after a state transition happens (and importantly before the next external event is processed), the state machine checks if any transitions get "enabled" by any such internal events. If there are any, then they are considered _triggered_ by an internal event, and those transitions happen immediately. When a statechart defines an activity or otherwise invokes a service, it can also declare the effect of the completion of the activity or service. - ## SCXML -In SCXML, the`In(state)` [conditional expression](https://www.w3.org/TR/scxml/#ConditionalExpressions) that can be used in guard functions. Technically, this is not an event, but such a guard ends up being evaluated whenever a state transition happens. +In SCXML, the`In(state)` [conditional expression](https://www.w3.org/TR/scxml/#ConditionalExpressions) that can be used in guard functions. Technically, this is not an event, but such a guard ends up being evaluated whenever a state transition happens. SCXML also [specifies a number of events](https://www.w3.org/TR/scxml/#errorsAndEvents) that are generated by the execution of a statechart: -* a `done.state.` event is generated whenever a [compound state](compound-state.html){:.glossary} reaches its [final state](final-state.html){:.glossary}. +- a `done.state.` event is generated whenever a [compound state](compound-state.html){:.glossary} reaches its [final state](final-state.html){:.glossary}. -* a `done.invoke.` event is generated whenever an invoke completes. +- a `done.invoke.` event is generated whenever an invoke completes. -* various `error` events +- various `error` events ## XState -XState [natively supports](https://xstate.js.org/docs/guides/guards.html#in-state-guards) `in: 'state'` guards defined on the transition. Technically, this is not its own event, but rather a side effect of a state transition. +XState [natively supports](https://xstate.js.org/docs/guides/guards.html#in-state-guards) `in: 'state'` guards defined on the transition. Technically, this is not its own event, but rather a side effect of a state transition. XState also supports -* `onDone` and `onError` event handlers for an [invoked service](https://xstate.js.org/docs/guides/communication.html#the-invoke-property). - -* a `done.state.` event is generated whenever a compound state reaches its [final state](https://xstate.js.org/docs/guides/final.html). +- `onDone` and `onError` event handlers for an [invoked service](https://xstate.js.org/docs/guides/communication.html#the-invoke-property). +- a `done.state.` event is generated whenever a compound state reaches its [final state](https://xstate.js.org/docs/guides/final.html). diff --git a/_glossary/parallel-state.md b/_glossary/parallel-state.md index 78ddc08..3372c9b 100644 --- a/_glossary/parallel-state.md +++ b/_glossary/parallel-state.md @@ -1,6 +1,6 @@ --- title: Parallel State -oneliner: a compound state that divides its substates into separate state machines that all get to be active at the same time +oneliner: A compound state that divides its substates into separate state machines that all get to be active at the same time keywords: - orthogonal - orthogonal state @@ -21,19 +21,19 @@ aka: # Parallel state -_Also known as **Orthogonal state** and **And state**_ +_Also known as **Orthogonal state** and **And state**_ -A parallel state is a [state](state.html){:.glossary} that is divided into separate regions. Each region contains more substates. When a parallel state is entered, _each_ of the regions are also entered; their [initial states](initial-state.html){:.glossary} are entered and so on. When a machine is _in_ a parallel state, _all_ of its regions are active. +A parallel state is a [state](state.html){:.glossary} that is divided into separate regions. Each region contains more substates. When a parallel state is entered, _each_ of the regions are also entered; their [initial states](initial-state.html){:.glossary} are entered and so on. When a machine is _in_ a parallel state, _all_ of its regions are active. ![An example of a parallel state](parallel.svg) -If the state above is entered, both B and D states are entered too. As you can see the _flick_ event is handled by both B _and_ D. If the _flick_ event happens, _both_ B and D get to "handle" the event, and the resulting state is C _and_ E. Note: It is not possible to send an event specifically to a single region. +If the state above is entered, both B and D states are entered too. As you can see the _flick_ event is handled by both B _and_ D. If the _flick_ event happens, _both_ B and D get to "handle" the event, and the resulting state is C _and_ E. Note: It is not possible to send an event specifically to a single region. -If another _flick_ event happens E will transition back to D, while C will ignore the event, since C doesn't handle the event. After 1 second, C will transition back to B, ready to handle the _flick_ event again. +If another _flick_ event happens E will transition back to D, while C will ignore the event, since C doesn't handle the event. After 1 second, C will transition back to B, ready to handle the _flick_ event again. ## Notation -A parallel state is like any other state, but it is subdivided into regions by way of straight, dashed lines. Each such region can then include states. +A parallel state is like any other state, but it is subdivided into regions by way of straight, dashed lines. Each such region can then include states. ![A state with four regions](parallel-notation.svg) @@ -43,34 +43,34 @@ Each of those regions can hold their own sets of states Parallel states are used when the machine needs to combine several discrete behaviours at the same time. -Without parallel states, modeling a set of four independent on/off switches (e.g. four independent toggle buttons with _on_ and _off_) would require 16 atomic states (`1on2on3on4on`, `1off2on3on4on`, `1on2off3on4on`, etc.) and 64 transitions (!) to be able to model all possible transitions. Increasing the data model from four to five toggle buttons _doubles_ the number of states (32) _and_ transitions (128). +Without parallel states, modeling a set of four independent on/off switches (e.g. four independent toggle buttons with _on_ and _off_) would require 16 atomic states (`1on2on3on4on`, `1off2on3on4on`, `1on2off3on4on`, etc.) and 64 transitions (!) to be able to model all possible transitions. Increasing the data model from four to five toggle buttons _doubles_ the number of states (32) _and_ transitions (128). -With parallel states, modeling the same set of four independent on/off-switches requires only eight states (four _on_ states and four _off_ states) and eight transitions (each state transitions to its counterpart). Increasing the data model from four to five toggle buttons increases the number of states and transitions by a fixed amount (two each). +With parallel states, modeling the same set of four independent on/off-switches requires only eight states (four _on_ states and four _off_ states) and eight transitions (each state transitions to its counterpart). Increasing the data model from four to five toggle buttons increases the number of states and transitions by a fixed amount (two each). -When events are completely independent of one another, parallel states shine. But by using [in guards](guard.html){:.glossary} it is possible to coordinate the different regions, and have states in one region _wait_ until another region reaches a particular state or raises an event. This makes it possible to model only partially independent behaviours using parallel states. +When events are completely independent of one another, parallel states shine. But by using [in guards](guard.html){:.glossary} it is possible to coordinate the different regions, and have states in one region _wait_ until another region reaches a particular state or raises an event. This makes it possible to model only partially independent behaviours using parallel states. ## SCXML -In Statechart XML, [the `` element](https://www.w3.org/TR/scxml/#parallel) declares a parallel state. It has more or less exactly the same set of attributes and elements as the `` element, except it has no _initial_ or _final_ states. The various _regions_ are defined by way of the direct child `` elements. +In Statechart XML, [the `` element](https://www.w3.org/TR/scxml/#parallel) declares a parallel state. It has more or less exactly the same set of attributes and elements as the `` element, except it has no _initial_ or _final_ states. The various _regions_ are defined by way of the direct child `` elements. This is a parallel state with two _regions_. When `p` becomes active, so does `foo1` and `bar1` -``` xml +```xml - - + + - - + + ``` ## XState -In XState, the state node must have `type: 'parallel'` (as of version 4.0) specified for a state to be marked as [a parallel state with regions](https://xstate.js.org/docs/guides/parallel.html). A parallel state can not define an `initial` property, since all regions are entered simultaneously. +In XState, the state node must have `type: 'parallel'` (as of version 4.0) specified for a state to be marked as [a parallel state with regions](https://xstate.js.org/docs/guides/parallel.html). A parallel state can not define an `initial` property, since all regions are entered simultaneously. ``` p: { diff --git a/_glossary/pseudostate.md b/_glossary/pseudostate.md index 0841b5c..113f454 100644 --- a/_glossary/pseudostate.md +++ b/_glossary/pseudostate.md @@ -1,6 +1,6 @@ --- title: Pseudostate -oneliner: A set of types of state that are transient in nature. +oneliner: A set of types of state that are transient in nature breadcrumbs: - id: state name: State @@ -12,5 +12,4 @@ aka: # Pseudostate -Statechart concepts include both concrete types of state, such as _atomic states_ and _compound states_ but also non-concrete, or abstract types of state such as _initial state_ and _history state_. A state machine can only be resting in such concrete states; pseudostates are therefore not _really_ states that the machine can be in, hence the prefix _pseudo-_. - +Statechart concepts include both concrete types of state, such as _atomic states_ and _compound states_ but also non-concrete, or abstract types of state such as _initial state_ and _history state_. A state machine can only be resting in such concrete states; pseudostates are therefore not _really_ states that the machine can be in, hence the prefix _pseudo-_. diff --git a/_glossary/raised-event.md b/_glossary/raised-event.md index 987c91b..eaa025c 100644 --- a/_glossary/raised-event.md +++ b/_glossary/raised-event.md @@ -11,15 +11,15 @@ date: 2019-05-08 # Raised Event -A raised event is a type of [generated event](generated-event.html){:.glossary} that is explicitly mentioned in the statechart. A raised event is usually declared as an [action](action.html){:.glossary} assigned to the [entry](enter.html){:.glossary} or [exit](exit.html){:.glossary} of a state, or on a [transition](transition.html){:.glossary} between states. +A raised event is a type of [generated event](generated-event.html){:.glossary} that is explicitly mentioned in the statechart. A raised event is usually declared as an [action](action.html){:.glossary} assigned to the [entry](enter.html){:.glossary} or [exit](exit.html){:.glossary} of a state, or on a [transition](transition.html){:.glossary} between states. ## Notation -A raised event is usually shown in the same manner as actions, in other words, after a slash. For example `entry / B`, which would mean that if this state was entered, the system would _generate_ the event `B` and process it as part of the current event, before processing any external events. +A raised event is usually shown in the same manner as actions, in other words, after a slash. For example `entry / B`, which would mean that if this state was entered, the system would _generate_ the event `B` and process it as part of the current event, before processing any external events. ## Usage -Raising events can be used as a way to coordinate different parts of a statechart. For example. exiting one state can trigger another transition in a different part of the statechart. +Raising events can be used as a way to coordinate different parts of a statechart. For example, exiting one state can trigger another transition in a different part of the statechart. ## SCXML @@ -27,13 +27,13 @@ In SCXML, an event can be raised using the `` element, wherever you can p ```xml - + ``` ## XState -In XState, an event can be raised by specifying the reserved action type `xstate.raise` +In XState, an event can be raised by specifying the reserved action type `xstate.raise`. ``` onEntry: { diff --git a/_glossary/refine.md b/_glossary/refine.md index 78a93e8..5284315 100644 --- a/_glossary/refine.md +++ b/_glossary/refine.md @@ -1,6 +1,6 @@ --- title: Refine -oneliner: The conversion of an atomic state to a compound state by the addition of substates. +oneliner: The conversion of an atomic state to a compound state by the addition of substates aka: - title: Specialise url: refine.html @@ -11,11 +11,11 @@ aka: **Also known as _specialise_** -Refinement of a state refers to the process of converting an [atomic state](atomic-state.html){:.glossary} to a [compound state](compound-state.html){:.glossary} by introducing substates. This is done to change the behaviour of the state in question, typically by virtue of the substates handling (and therefore consuming) some, or all events before the refined state. +Refinement of a state refers to the process of converting an [atomic state](atomic-state.html){:.glossary} to a [compound state](compound-state.html){:.glossary} by introducing substates. This is done to change the behaviour of the state in question, typically by virtue of the substates handling (and therefore consuming) some, or all events before the refined state. ## Example -For example, a state A could react to the event ε1 by transitioning to the state B, and react to the event ε2 by transitioning to state C . If it is desirable that the ε2 transition to state C should only be valid after, say, one second, the atomic state A can be converted to a compound state A by introducing a couple of substates. The ε2 event from A to C could then be moved to one of those substates. +For example, a state A could react to the event ε1 by transitioning to the state B, and react to the event ε2 by transitioning to state C . If it is desirable that the ε2 transition to state C should only be valid after, say, one second, the atomic state A can be converted to a compound state A by introducing a couple of substates. The ε2 event from A to C could then be moved to one of those substates. The act of introducing these substates to change A's behaviour, is called a _refining_, or _specialising_ state A. diff --git a/_glossary/self-transition.md b/_glossary/self-transition.md index cbc71d5..af5e43d 100644 --- a/_glossary/self-transition.md +++ b/_glossary/self-transition.md @@ -1,6 +1,6 @@ --- title: Self transition -oneliner: A transition from a state back to itself. +oneliner: A transition from a state back to itself breadcrumbs: - id: transition name: Transition @@ -10,7 +10,7 @@ keywords: # Self transition -A self transition is a [transition](transition.html){:.glossary} that starts and ends in the same state. When a self transition is taken, the state in question is [exited](exit.html){:.glossary} and [entered](enter.html){:.glossary} again. +A self transition is a [transition](transition.html){:.glossary} that starts and ends in the same state. When a self transition is taken, the state in question is [exited](exit.html){:.glossary} and [entered](enter.html){:.glossary} again. ## Notation @@ -20,11 +20,11 @@ A self transition is depicted as a segment of a circle, here with the event _aga ## Usage -Self transitions are commonly used to "restart" the current state, causing the exit actions to happen, followed by the entry actions. This also resets the timer for how long the machine has been in the state, meaning that [delayed transitions](delayed-transition.html){:.glossary} start counting from 0 again. +Self transitions are commonly used to "restart" the current state, causing the exit actions to happen, followed by the entry actions. This also resets the timer for how long the machine has been in the state, meaning that [delayed transitions](delayed-transition.html){:.glossary} start counting from 0 again. -It is important to note that self transitions (or transitions to own child states) will in fact _exit_ the state in which the transition starts. This is important to keep in mind and can be a source of confusion, since it leads to the _exit_ and _entry_ actions of the state to be re-executed. +It is important to note that self transitions (or transitions to own child states) will in fact _exit_ the state in which the transition starts. This is important to keep in mind and can be a source of confusion, since it leads to the _exit_ and _entry_ actions of the state to be re-executed. -For [compound states](compound-state.html){:.glossary} this means that all substates are exited, and its [initial state](initial-state.html){:.glossary} is entered. This effectively restarts the compound state itself. +For [compound states](compound-state.html){:.glossary} this means that all substates are exited, and its [initial state](initial-state.html){:.glossary} is entered. This effectively restarts the compound state itself. ## SCXML @@ -32,7 +32,7 @@ In Statechart XML, a self transition uses the standard `` syntax, wi ```xml - + ``` diff --git a/_glossary/state.md b/_glossary/state.md index 874acea..c7755c8 100644 --- a/_glossary/state.md +++ b/_glossary/state.md @@ -5,63 +5,62 @@ oneliner: A particular behaviour of a state machine # State -A state in a state machine describes a particular behaviour of the machine. When we say that a machine is "in" a state, it means that the machine behaves in the way that state describes. +A state in a state machine describes a particular behaviour of the machine. When we say that a machine is "in" a state, it means that the machine behaves in the way that state describes. -The behaviour of a state is defined as how the state reacts to [events](event.html){:.glossary}. Each state can specify a number of events that it "understands", and specifies, for each event, any number of [transitions](transition.html){:.glossary} that could be taken, if that event happens. A state also describes a set of [actions](action.html){:.glossary} to execute when the state is entered or exited, and in some implementations, [activities](activity.html){:.glossary} that should be happening for the entire duration of time that the machine is _in_ the state. +The behaviour of a state is defined as how the state reacts to [events](event.html){:.glossary}. Each state can specify a number of events that it "understands", and specifies, for each event, any number of [transitions](transition.html){:.glossary} that could be taken, if that event happens. A state also describes a set of [actions](action.html){:.glossary} to execute when the state is entered or exited, and in some implementations, [activities](activity.html){:.glossary} that should be happening for the entire duration of time that the machine is _in_ the state. A state usually has a name, but some systems allow for anonymous states. -When a state machine is _executed_, it [enters](enter.html){:.glossary} the [initial state](initial-state.html){:.glossary}, and adopts the behaviour associated with that state. As the machine handles events, it transitions to other states, thus changing the behaviour of the machine over time. - +When a state machine is _executed_, it [enters](enter.html){:.glossary} the [initial state](initial-state.html){:.glossary}, and adopts the behaviour associated with that state. As the machine handles events, it transitions to other states, thus changing the behaviour of the machine over time. ## States in statcharts There are a few special types of states in statecharts: -* [Atomic](atomic-state.html){:.glossary} (or simple) states, are states that have no substates. -* [Compound](compound-state.html){:.glossary} (or composite) states, are states that have one or more substates. The substates can be said to be a _refinement_ of their parent state. Coversely, the parent state can be said to provide a _default behaviour_ of the substates. -* [Parallel](parallel-state.html){:.glossary} (or orthogonal) states, are states that (like a compound states) have substates, but where the child states are grouped into orthogonal regions, and those regions are all active simultaneously. +- [Atomic](atomic-state.html){:.glossary} (or simple) states, are states that have no substates. +- [Compound](compound-state.html){:.glossary} (or composite) states, are states that have one or more substates. The substates can be said to be a _refinement_ of their parent state. Conversly, the parent state can be said to provide a _default behaviour_ of the substates. +- [Parallel](parallel-state.html){:.glossary} (or orthogonal) states, are states that (like compound states) have substates, but where the child states are grouped into orthogonal regions, and those regions are all active simultaneously. ## Hierarchy -These constructs allow statecharts to be hierarchically organized. When a compound state is active (when the machine is _in_ that state), exactly one of its child states are also active. When a parallel state is active, by definition, all of the child states (called regions) are also active. This implies that in a hierarchically organized statechart, many states can be active at any point in time. Specifically, for each active atomic state, all the compound and parallel parents are also deemed to be "active". When an event happens, the "deepest" of the states get to handle the event, pre-empting the behaviour of the parent states that contain it. +These constructs allow statecharts to be hierarchically organized. When a compound state is active (when the machine is _in_ that state), exactly one of its child states is also active. When a parallel state is active, by definition, all of the child states (called regions) are also active. This implies that in a hierarchically organized statechart, many states can be active at any point in time. Specifically, for each active atomic state, all the compound and parallel parents are also deemed to be "active". When an event happens, the "deepest" of the states get to handle the event, pre-empting the behaviour of the parent states that contain it. ## Notation -A state is generally depicted as a rounded box, with the name of the state centered at the top of the box, with a horizontal line sparating the name of the state from the actions and activities. The original statechart paper did not have these lines, they come from the UML Statechart standard. It is common to drop this line in states that have no substates and no actions. +A state is generally depicted as a rounded box, with the name of the state centered at the top of the box, with a horizontal line sparating the name of the state from the actions and activities. The original statechart paper did not have these lines, they come from the UML Statechart standard. It is common to drop this line in states that have no substates and no actions. -Transitions _from_ this state are depicted as curvy lines pointing away. Transitions _to_ this state are depicted as curved lines that point to this state. +Transitions _from_ this state are depicted as curvy lines pointing away. Transitions _to_ this state are depicted as curved lines that point to this state. -Entry and Exit actions are written in text form inside the state. +Entry and exit actions are written in text form inside the state. ![A rounded rectangle with a horizontal line, on top of which the word "On", and below which the words "entry / turn on"](state.svg) ## SCXML -In SCXML, a state is defined by the `` element, with the name of the state in its `id` attribute. An empty state: +In SCXML, a state is defined by the `` element, with the name of the state in its `id` attribute. An empty state: -``` xml - +```xml + ``` The child elements of the `` include entry and exit actions, transitions, along with substates: -``` xml +```xml - + - + - + ``` ## XState -In XState, a state node is specified as a named object in the `states` property of a state machine or compound state node. The key becomes the name of the state node (and is used to construct the state node's ID), and the object defines the state's behaviour. +In XState, a state node is specified as a named object in the `states` property of a state machine or compound state node. The key becomes the name of the state node (and is used to construct the state node's ID), and the object defines the state's behaviour. Here's an empty state called `my_state`: @@ -100,9 +99,9 @@ Here's the `my_state` with an action, transitions, and substates: In SCION Core, a state is described by an object with an `id` property, denoting the identifier of the state. -``` javascript +```javascript { - id: 'my_state'; + id: 'my_state' } ``` diff --git a/_glossary/transition.md b/_glossary/transition.md index 3b304cf..43178e1 100644 --- a/_glossary/transition.md +++ b/_glossary/transition.md @@ -1,25 +1,25 @@ --- title: Transition -oneliner: The instantaneous transfer from one state to another. +oneliner: The instantaneous transfer from one state to another --- # Transition -In an executing state machine, a transition is the instantaneous transfer from one [state](state.html){:.glossary} to another. In a state machine, a transition tells us what happens when an [event](event.html){:.glossary} occurs. +In an executing state machine, a transition is the instantaneous transfer from one [state](state.html){:.glossary} to another. In a state machine, a transition tells us what happens when an [event](event.html){:.glossary} occurs. -When an event happens, the currently active state(s) are inspected, looking for an outbound transition that could be triggered by the event. If it is [guarded](guard.html){:.glossary}, the guard is checked, and the machine keeps looking for a transition. At last, if a transition is found, it is then _executed_ by [exiting](exit.html){:.glossary} the state in question, and [entering](enter.html){:.glossary} the state that the transition points to. +When an event happens, the currently active state(s) are inspected, looking for an outbound transition that could be triggered by the event. If it is [guarded](guard.html){:.glossary}, the guard is checked, and the machine keeps looking for a transition. At last, if a transition is found, it is then _executed_ by [exiting](exit.html){:.glossary} the state in question, and [entering](enter.html){:.glossary} the state that the transition points to. A [self transition](self-transition.html){:.glossary} is a transition that goes from and to the same state. A [local transition](local-transition.html){:.glossary} is a transition that does not exit the source state, but the target state _must_ be a substate of the source state. -An [automatic transition](automatic-transition.html){:.glossary} is a transition that is not tied to any particular event, but rather tries to fire at all times. Automatic transitions are usually guarded. +An [automatic transition](automatic-transition.html){:.glossary} is a transition that is not tied to any particular event, but rather tries to fire at all times. Automatic transitions are usually guarded. A transition can define [actions](action.html){:.glossary} that will be executed whenever that transition is executed. ## Notation -A transition is depicted as a curved arrow. The name of the event that triggers the transition is written close by, perhaps on top of the arrow. +A transition is depicted as a curved arrow. The name of the event that triggers the transition is written close by, perhaps on top of the arrow. ![A transition, for the event _my_event_](event-arrow.svg) @@ -27,19 +27,19 @@ The transition always sits between two distinct states ![A transition from _somestate_ to _otherstate_ given the _my_event_ event](event.svg) -The state that the arrow points _from_ is the state in which the event in question is handled. The state that the arrow points _to_ is the state that the state machine ends up in, if the transition is executed. +The state that the arrow points _from_ is the state in which the event in question is handled. The state that the arrow points _to_ is the state that the state machine ends up in, if the transition is executed. -A transition can have multiple targets, in case of targetting different regions of a [parallel state](parallel-state.html){:.glossary} +A transition can have multiple targets, in case of targetting different regions of a [parallel state](parallel-state.html){:.glossary}. ## Usage -Alongside _states_, transitions are the main ingredient of state machines and statecharts. A single transition defines how the machine might react to a particular event in case the machine is in a particular state. The combination of the different states and their transition together make up the statechart's behaviour. +Alongside _states_, transitions are the main ingredient of state machines and statecharts. A single transition defines how the machine might react to a particular event in case the machine is in a particular state. The combination of the different states and their transition together make up the statechart's behaviour. Transitions are primarily used to move the machine between states, possibly executing _transition actions_ if they are defined. ## SCXML -In Statechart XML, a transition is a `` element nested inside the `` element to which it applies. The `target` attribute identifies the state to which it points, while `cond` is used for the guard. +In Statechart XML, a transition is a `` element nested inside the `` element to which it applies. The `target` attribute identifies the state to which it points, while `cond` is used for the guard. ```xml @@ -48,7 +48,7 @@ In Statechart XML, a transition is a `` element nested inside the `< ``` -Multiple transitions for the same event are defined by defining several transitions for the same event. The transitions are inspected in _document order_: +Multiple transitions for the same event are defined by defining several transitions for the same event. The transitions are inspected in _document order_: ```xml @@ -61,7 +61,7 @@ Multiple transitions for the same event are defined by defining several transiti ## XState -In XState, a transition is defined using [the *on* property](https://xstate.js.org/docs/guides/transitions.html) of a state. The key is the event in question, and the value is the name of the target state: +In XState, a transition is defined using [the _on_ property](https://xstate.js.org/docs/guides/transitions.html) of a state. The key is the event in question, and the value is the name of the target state: ```json "somestate": { @@ -72,7 +72,7 @@ In XState, a transition is defined using [the *on* property](https://xstate.js.o "otherstate": {} ``` -It is also possible to define an array of transitions for any given event. Each element in the array defines a transition to be handled by that event, and they are inspected in array order: +It is also possible to define an array of transitions for any given event. Each element in the array defines a transition to be handled by that event, and they are inspected in array order: ```json "somestate": { @@ -86,4 +86,3 @@ It is also possible to define an array of transitions for any given event. Each "otherstate": {}, "thirdstate": {} ``` - diff --git a/how-to-use-statecharts.md b/how-to-use-statecharts.md index 26e3e68..2eae0e7 100644 --- a/how-to-use-statecharts.md +++ b/how-to-use-statecharts.md @@ -8,19 +8,19 @@ This page tries to describe some aspects of employing statecharts in your day-to ## Determine scope -When you first learn about statecharts, you might get the feeling that statecharts can be used to describe the _entire_ behaviour of an application, all the way from which screens show as part of logging in, to the state of each checkbox and text field in every screen, all represented in one big statechart. That would be a statechart from hell, and an even bigger maintenance burden. Instead, the focus should be to get a grip on the **behaviour at the component level**, whatever a component might be. A single screen would be a component, for sure. A single text field that might have some particular internal behaviour (e.g. it changes color based on various flags like _empty_, _required_ or _invalid_) might warrant that to be wrapped in a component with a statechart to describe its behaviour. +When you first learn about statecharts, you might get the feeling that statecharts can be used to describe the _entire_ behaviour of an application, all the way from which screens show as part of logging in, to the state of each checkbox and text field in every screen, all represented in one big statechart. That would be a statechart from hell, and an even bigger maintenance burden. Instead, the focus should be to get a grip on the **behaviour at the component level**, whatever a component might be. A single screen would be a component, for sure. A single text field that might have some particular internal behaviour (e.g. it changes color based on various flags like _empty_, _required_ or _invalid_) might warrant that to be wrapped in a component with a statechart to describe its behaviour. -In general, structure your code as you did before, by dividing things up into components. Use statecharts to describe the behaviour of each component in isolation. Use events and so on to get communication between components going just like before; keep the statechart _internal_ to the component itself. The _user_ of the component shouldn't need to know that there's a statechart within it, other than by inspecting the code or guessing (since it behaves so well). +In general, structure your code as you did before, by dividing things up into components. Use statecharts to describe the behaviour of each component in isolation. Use events and so on to get communication between components going just like before; keep the statechart _internal_ to the component itself. The _user_ of the component shouldn't need to know that there's a statechart within it, other than by inspecting the code or guessing (since it behaves so well). ## Starting from scratch or not -If you're starting from scratch, and you envision the component you're building to have any form of difference of behaviour after it's instantiated (most do), then consider adding a simple statechart from the get-go, and start out with letting the statechart determine which behaviour is appropriate. Even if you don't have the other behaviour available, it's always easier if the component already has a simple (one-state?) statechart. +If you're starting from scratch, and you envision the component you're building to have any form of difference of behaviour after it's instantiated (most do), then consider adding a simple statechart from the get-go, and start out with letting the statechart determine which behaviour is appropriate. Even if you don't have the other behaviour available, it's always easier if the component already has a simple (one-state?) statechart. -If you're not starting from scratch, it means you have code that reacts to events already. Speaking generally, when your code receives an action, it usually checks some booleans or the value of some internal state (e.g. if a value is greater than some threshold) before performing some updates. It is this code that the introduction of statecharts aims to replace. Throughout your event handlers, those conditionals all make up the _implicit_ state machine. After introducing statecharts, you should end up with more or less the same state machine, but an _explicit_ one. +If you're not starting from scratch, it means you have code that reacts to events already. Speaking generally, when your code receives an action, it usually checks some booleans or the value of some internal state (e.g. if a value is greater than some threshold) before performing some updates. It is this code that the introduction of statecharts aims to replace. Throughout your event handlers, those conditionals all make up the _implicit_ state machine. After introducing statecharts, you should end up with more or less the same state machine, but an _explicit_ one. ## Distill the API between the component and the statechart -To start using a statechart, the tangled mess that might be your component and its different behaviours need to be disentangled: The _what_ / _how_ needs to be separated from the _why_. You should end up with event handlers that send the event to the statechart, and then does what the statechart says to do, typically by invoking "actionable" items on behalf of the statechart. +To start using a statechart, the tangled mess that might be your component and its different behaviours need to be disentangled: The _what_/_how_ needs to be separated from the _why_. You should end up with event handlers that send the event to the statechart, and then does what the statechart says to do, typically by invoking "actionable" items on behalf of the statechart. Before the introduction of a statechart, you will have code with the following structure: @@ -28,7 +28,7 @@ Before the introduction of a statechart, you will have code with the following s - The event handler checks some booleans - Based on those booleans, you decide to do some real work -Those booleans end up being littered throughout various event handlers, for better or worse. It's called the "Event-Action" paradigm of programming. +Those booleans end up being littered throughout various event handlers, for better or worse. It's called the "Event-Action" paradigm of programming. After introducing statecharts, you should have code with the following structure: @@ -46,63 +46,64 @@ The communication between your component and the statechart therefore happens as - The statechart tells your object to perform some [action](glossary/action.html){:.glossary} or control some [activity](glossary/activity.html){:.glossary} — e.g. tell the field that it's "invalid", or start parsing the results, or stop a HTTP request. - The statechart tells you _which new state_ your component is in. -These are the four touchpoints between the statechart and the outside world (your component). Statecharts fit into an event driven system. It accepts events, and turns them into actions. +These are the four touchpoints between the statechart and the outside world (your component). Statecharts fit into an event driven system. It accepts events, and turns them into actions. ## Designing a statechart This is the biggest hurdle if you're new to statecharts, mainly because it is often such a foreign concept. You need to decide on the notation, if you want the benefit of a graphical representation, and ultimately which tools you'll choose. -Tools aside, the process of designing a component's statechart is to start by discovering the _modes_ of the component in question. Those are good candidates for top level states of your statechart. Next, you can start to think about what events that take you between those states. Remember, at the "top level" things don't need to be 100% precise; this is what the substates are for. +Tools aside, the process of designing a component's statechart is to start by discovering the _modes_ of the component in question. Those are good candidates for top level states of your statechart. Next, you can start to think about what events that take you between those states. Remember, at the "top level" things don't need to be 100% precise; this is what the substates are for. -Once you have some high level states and what events cause the behaviour to change, you can add this to the statechart, and your code will be better off for it. Later, we will show how you can move more things into the states. +Once you have some high level states and what events cause the behaviour to change, you can add this to the statechart, and your code will be better off for it. Later, we will show how you can move more things into the states. ## Example -In order to explain this process, I'm going to try to walk you through the process that went into the design of the following component: A simple search form. It is modeled after the same UI that is presented in [Robust React User Interfaces with Finite State Machines](https://css-tricks.com/robust-react-user-interfaces-with-finite-state-machines/). +In order to explain this process, I'm going to try to walk you through the process that went into the design of the following component: A simple search form. It is modeled after the same UI that is presented in [Robust React User Interfaces with Finite State Machines](https://css-tricks.com/robust-react-user-interfaces-with-finite-state-machines/). To repeat the requirements from that article: -> * Show a search input and a search button that allows the user to search for photos -> * When the search button is clicked, fetch photos with the search term from Flickr -> * Display the search results in a grid of small sized photos -> * When a photo is clicked/tapped, show the full size photo -> * When a full-sized photo is clicked/tapped again, go back to the gallery view +> - Show a search input and a search button that allows the user to search for photos +> - When the search button is clicked, fetch photos with the search term from Flickr +> - Display the search results in a grid of small sized photos +> - When a photo is clicked/tapped, show the full size photo +> - When a full-sized photo is clicked/tapped again, go back to the gallery view Off the top of my head I can think of the following top level "modes" or "states" of this application: -* Initial — no search results are available -* Searching — when the search button was clicked -* Displaying results — when displaying results -* Zoomed in — when a photo is zoomed in on +- Initial — no search results are available +- Searching — when the search button was clicked +- Displaying results — when displaying results +- Zoomed in — when a photo is zoomed in on If I put my statechart hat on, these can be thought of as "top level states": ![Initial set of states](how-to-use-statecharts-initial-states.svg) -Note that this set of states might change, since we haven't really understood what a "mode" is. The main thing to look for is a different _behaviour_, i.e. that the component in question _reacts in a different way_ to events. For example, the search app should react differently to a click on a photo when displaying results vs when zoomed in on a photo. +Note that this set of states might change, since we haven't really understood what a "mode" is. The main thing to look for is a different _behaviour_, i.e. that the component in question _reacts in a different way_ to events. For example, the search app should react differently to a click on a photo when displaying results vs when zoomed in on a photo. -From the requirements it's also pretty easy to think up the transitions between those states too. Here's the "happy path": +From the requirements it's also pretty easy to think up the transitions between those states too. Here's the "happy path": -* Initial → Searching: someone typed something and hit the _Search_ button — I'll call this the **search** event -* Searching → Displaying results: the HTTP request completed with some data, the UI can be populated with stuff—I'll call this the **results** event. -* Displaying results → Zoomed in: The user clicked a photo and we now _zoom in_ on a particular photo. I'll call this the **zoom** event -* Zoomed in → Displaying results: The user clicked a zoomed in photo and we now _zoom out_ back to the results. I can call this the **zoom_out** event. +- Initial → Searching: someone typed something and hit the _Search_ button — I'll call this the **search** event +- Searching → Displaying results: the HTTP request completed with some data, the UI can be populated with stuff—I'll call this the **results** event. +- Displaying results → Zoomed in: The user clicked a photo and we now _zoom in_ on a particular photo. I'll call this the **zoom** event +- Zoomed in → Displaying results: The user clicked a zoomed in photo and we now _zoom out_ back to the results. I can call this the **zoom_out** event. Again, these can be drawn into the statechart's "top level": ![Initial set of states with transitions](how-to-use-statecharts-initial-transitions.svg) -> *Now, if you're an experienced statechart designer, you can probably already see one big shortcoming of this statechart. Luckily, with a diagram, they are extremely easy to discover: There is no way to get from the "results" state back into the searching state. There are no direct arrows, and there is no path to get there. (It was also not explicitly stated in the requirements document!) I'm going to ignore this problem for now, because I want to show (later) how you can fix such problems _purely_ by making changes to the state machine. So, if you spotted this by yourself, pat yourself on the back now.* +> _Now, if you're an experienced statechart designer, you can probably already see one big shortcoming of this statechart. Luckily, with a diagram, they are extremely easy to discover: There is no way to get from the "results" state back into the searching state. There are no direct arrows, and there is no path to get there. (It was also not explicitly stated in the requirements document!) I'm going to ignore this problem for now, because I want to show (later) how you can fix such problems **purely** by making changes to the state machine. So, if you spotted this by yourself, pat yourself on the back now._ ### Initial implementation -At this point we have enough stuff to work on to be able to get an initial implementation running too, just to get the happy path running. We can then check them off the list of "problems" that typically plague a quick implementation. +At this point we have enough stuff to work on to be able to get an initial implementation running too, just to get the happy path running. We can then check them off the list of "problems" that typically plague a quick implementation. -I will show the statecharts in both SCXML and XState flavours. Both will give us nice diagrams and they're both _executable_ statecharts, meaning I don't have to do any manual translation from this representation to code. +I will show the statecharts in both SCXML and XState flavours. Both will give us nice diagrams and they're both _executable_ statecharts, meaning I don't have to do any manual translation from this representation to code. First off, the top level states: SCXML: + ```xml @@ -137,16 +138,16 @@ SCXML: ```xml - + - + - + - + ``` @@ -185,88 +186,66 @@ XState: If we look at our _API surface_—the set of events, guards and actions that we have—we can start to compile a list of things that our UI needs to provide: -* Events: `search`, `results`, `zoom`, and `zoom_out` -* States: `initial`, `searching`, `showing_results` and `zoomed_in` +- Events: `search`, `results`, `zoom`, and `zoom_out` +- States: `initial`, `searching`, `showing_results` and `zoomed_in` #### Absence of data transfer Note the absence of any data being passed back and forth: The events themselves are pretty anonymous; this is about high level things that happen in the UI. -This absence of data transfer also means that the component still needs to keep track of the "business state" — the variables and stuff that the component is busy working on. The statechart doesn't need or care about those things, it is only concerned with triggering the right actions at the right times. +This absence of data transfer also means that the component still needs to keep track of the "business state" — the variables and stuff that the component is busy working on. The statechart doesn't need or care about those things, it is only concerned with triggering the right actions at the right times. ### Events -The first part of the API is actually sending events _from_ the world and _to_ the state machine. This is also somewhat independent of the actual state machine library, as most of them take an event name in the form of a string. +The first part of the API is actually sending events _from_ the world and _to_ the state machine. This is also somewhat independent of the actual state machine library, as most of them take an event name in the form of a string. -To recap the events were: `search`, `results`, `zoom`, and `zoom_out` — these are all things that "happen" in the real world, that need to _tell the statechart_ about +To recap the events were: `search`, `results`, `zoom`, and `zoom_out` — these are all things that "happen" in the real world, that need to _tell the statechart_ about. We want something that's similar to this: ```js -searchButton.onclick = statemachine.send("search"); -httpRequest.then(() => statemachine.send("results")); -resultspanel.onclick = statemachine.send("zoom_in"); -zoomedphoto.onclick = statemachine.send("zoom_out"); +searchButton.onclick = statemachine.send('search') +httpRequest.then(() => statemachine.send('results')) +resultspanel.onclick = statemachine.send('zoom_in') +zoomedphoto.onclick = statemachine.send('zoom_out') ``` -Essentially the buttons, HTTP requests and other things that _generate events_ don't need to know what's going to happen; they shouldn't. They should just blindly send the information to the state machine, and let the state machine figure out what to do. +Essentially the buttons, HTTP requests and other things that _generate events_ don't need to know what's going to happen; they shouldn't. They should just blindly send the information to the state machine, and let the state machine figure out what to do. ### States -Finally, the component is allowed to see which state is the "current" state. This of course depends on the statechart library, but most will happily tell you the _name_ of the current state, typically as a string: +Finally, the component is allowed to see which state is the "current" state. This of course depends on the statechart library, but most will happily tell you the _name_ of the current state, typically as a string: ```js -statemachine.currentState.value; // "searching" +statemachine.currentState.value // "searching" ``` ## Implementation We've now explained all of the parts necessary to get our UI and the component talking together. - - - - - - - - - - - - - - - - - - - - - - ### Decouple the component from the statechart? -At this point in time I think it's very useful to point out dependency that might come creeping. The component in question easily becomes dependent on the states in the statechart. A decision has to be made, or else it will be made for you. +At this point in time I think it's very useful to point out dependency that might come creeping. The component in question easily becomes dependent on the states in the statechart. A decision has to be made, or else it will be made for you. -The statechart invariably starts out as a reflection of the _modes_ that the component has, e.g. enabled, disabled, loading and so on. It is therefore common to use the "current state" of the statechart to reflect it in the component somewhere. For a HTML based component, this might translate into a top level CSS element, like `state-enabled` and `state-loading`. For a React app, it might be that your app renders different things based on the top level state. This is completely natural, and introduces an implied coupling between the statechart and the component. +The statechart invariably starts out as a reflection of the _modes_ that the component has, e.g. enabled, disabled, loading and so on. It is therefore common to use the "current state" of the statechart to reflect it in the component somewhere. For a HTML based component, this might translate into a top level CSS element, like `state-enabled` and `state-loading`. For a React app, it might be that your app renders different things based on the top level state. This is completely natural, and introduces an implied coupling between the statechart and the component. -This coupling may or may not be beneficial, depending on how you end up using the statechart, but you should be aware of the coupling and the problems it introduces. +This coupling may or may not be beneficial, depending on how you end up using the statechart, but you should be aware of the coupling and the problems it introduces. -| Decoupled | Coupled | -| --------- | -------- | -| The component **doesn't know** which state it's in | The component **knows** which state it's in | -| The component is explicitly _told_ when to change its mode, because the statechart says when _entering_ this state, _enter this mode_ | The component changes its mode automatically: whenever the statechart has handled an event, the component asks the statechart which state it's in and uses that | -| The component is explicitly _told_ when to do stuff, because the statechart says when _entering_ this state, _do this_ | The component does things based on the "current state": Whenever the statechart has handled an event, the component asks the statechart which state it's in and executes various functions | +| Decoupled | Coupled | +| ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| The component **doesn't know** which state it's in | The component **knows** which state it's in | +| The component is explicitly _told_ when to change its mode, because the statechart says when _entering_ this state, _enter this mode_ | The component changes its mode automatically: whenever the statechart has handled an event, the component asks the statechart which state it's in and uses that | +| The component is explicitly _told_ when to do stuff, because the statechart says when _entering_ this state, _do this_ | The component does things based on the "current state": Whenever the statechart has handled an event, the component asks the statechart which state it's in and executes various functions | -* If you decide to keep them **decoupled**, it comes at the increased cost of having to define additional actions—an increased "API surface" if you will. Additionally the statechart needs to have entry handlers (and possibly exit handlers) to turn on (and off) modes in the component. The statechart needs to be able to control _explicitly_ what the component should be doing at any time. -* If you decide to keep them **coupled**, it comes at the cost of being unable to make changes to the statechart itself. Introducing a new state to make a behavioural change can no longer be done _purely_ on the statechart side, because this new state might _affect the component_ when it **should not**, or it might _not affect it_ when it **should**. Often a change in the statechart has to be done along with a change in the component. +- If you decide to keep them **decoupled**, it comes at the increased cost of having to define additional actions—an increased "API surface" if you will. Additionally the statechart needs to have entry handlers (and possibly exit handlers) to turn on (and off) modes in the component. The statechart needs to be able to control _explicitly_ what the component should be doing at any time. +- If you decide to keep them **coupled**, it comes at the cost of being unable to make changes to the statechart itself. Introducing a new state to make a behavioural change can no longer be done _purely_ on the statechart side, because this new state might _affect the component_ when it **should not**, or it might _not affect it_ when it **should**. Often a change in the statechart has to be done along with a change in the component. ### Decoupled statecharts FTW When starting out with statecharts, it's often easiest to get going with **coupled** statecharts, since it's one less thing to learn. -For long term durability and maintainability of statecharts, it is probably best to go **decoupled**: remove the direct dependency between the statechart and the component. If you choose to do this, there are some notable things that the component _should not_ be worrying about, such as: +For long term durability and maintainability of statecharts, it is probably best to go **decoupled**: remove the direct dependency between the statechart and the component. If you choose to do this, there are some notable things that the component _should not_ be worrying about, such as: - _Which state is the statechart in?_ — It really doesn't matter. What matters are the actions that are called. - _Which transition just fired?_ — This too doesn't matter. @@ -275,11 +254,11 @@ The things that matter in a decoupled statechart are: events, guards and actions ## Coding up the API -The choice of UI framework should really not be very important, since what we're trying to describe is _what to do_ when certain events happen. We aim to replace code that litter the UI code to hide and show components based on the "state" of various variables, and replace them with actions that control the behaviour of the UI component. +The choice of UI framework should really not be very important, since what we're trying to describe is _what to do_ when certain events happen. We aim to replace code that litter the UI code to hide and show components based on the "state" of various variables, and replace them with actions that control the behaviour of the UI component. ### Actions -First off, let's take a look at the actions; the desired output of the state machine. We need a component that can _react_ to what the state machine tells it to do. +First off, let's take a look at the actions; the desired output of the state machine. We need a component that can _react_ to what the state machine tells it to do. To recap, the actions that the state machine can perform are: `startHttpRequest`, `cancelHttpRequest`, `showResults`, `zoomIn`, and `zoomOut` @@ -287,26 +266,25 @@ A simple `performAction` function can do nicely: ```js function performAction(event) { - if (event === "startHttpRequest") { + if (event === 'startHttpRequest') { // get value of text field // construct URL // send HTTP request } - if (event === "cancelHttpRequest") { + if (event === 'cancelHttpRequest') { // cancel HTTP request (if it's still active) } - if (event === "showResults") { + if (event === 'showResults') { } - if (event === "zoomIn") { + if (event === 'zoomIn') { } - if (event === "zoomOut") { + if (event === 'zoomOut') { } } ``` -Forgive the naive if handler, as I opted for something readable. I'll leave it to the readers to figure out ways of improving this code. - +Forgive the naive if handler, as I opted for something readable. I'll leave it to the readers to figure out ways of improving this code. diff --git a/use-case-statecharts-in-user-interfaces.md b/use-case-statecharts-in-user-interfaces.md index 52c7e95..e8d20c8 100644 --- a/use-case-statecharts-in-user-interfaces.md +++ b/use-case-statecharts-in-user-interfaces.md @@ -1,47 +1,47 @@ # Statecharts in User Interfaces -Statecharts are a good fit for solving certain problems with coding user interfaces. Both statecharts and most UI technologies are event driven, but the technologies complement one another very well. +Statecharts are a good fit for solving certain problems with coding user interfaces. Both statecharts and most UI technologies are event driven, but the technologies complement one another very well. ## Event Action paradigm -Most UIs are event driven, meaning that the user interface components themselves generate events when the user interacts with them. These events typically trigger actions attached directly to the UI components. In some systems, the events are passed around things like component hierarchies, or forwarded to an event loop that gets to decide what _action_ to perform. The implementation of the UI component somehow decides what to do, what to show, what to stop doing and what to stop showing. +Most UIs are event driven, meaning that the user interface components themselves generate events when the user interacts with them. These events typically trigger actions attached directly to the UI components. In some systems, the events are passed around things like component hierarchies, or forwarded to an event loop that gets to decide what _action_ to perform. The implementation of the UI component somehow decides what to do, what to show, what to stop doing and what to stop showing. This is called the **Event Action** paradigm, because the action is tightly coupled to the event. -Statecharts inject themselves between the event being generated and the action being performed. A statechart's main function is to take an event created somewhere, and make a decision on _doing stuff_. It does precisely what most event driven UIs _don't_ do, or provide a framework for. +Statecharts inject themselves between the event being generated and the action being performed. A statechart's main function is to take an event created somewhere, and make a decision on _doing stuff_. It does precisely what most event driven UIs _don't_ do, or provide a framework for. ## A comparison -In a traditional event driven user interface component, say, the ubiquitous HTML `` element, the UI component generates a lot of events, that the developer can subscribe to, from gaining or losing focus, editing, selecting, mouse movement and so on. A developer has a plethora of events to choose between. There is a similar almost infinite set of things that can be _done_ to a user interface. Each element has a wide range of possible mutations. It is up to the developer to decide _what to do_ based on whatever event that happens. +In a traditional event driven user interface component, say, the ubiquitous HTML `` element, the UI component generates a lot of events, that the developer can subscribe to, from gaining or losing focus, editing, selecting, mouse movement and so on. A developer has a plethora of events to choose between. There is a similar almost infinite set of things that can be _done_ to a user interface. Each element has a wide range of possible mutations. It is up to the developer to decide _what to do_ based on whatever event that happens. -In simple user interfaces, the event-action paradigm is fine. Here, for example is an input handler that turns the text green when text is entered into the field: +In simple user interfaces, the event-action paradigm is fine. Here, for example is an input handler that turns the text green when text is entered into the field: ```css .green { - box-shadow: 0 0 30px green + box-shadow: 0 0 30px green; } ``` ```html - + ``` ```js -var field = document.getElementById("my_editor"); +var field = document.getElementById('my_editor') function handleChange(e) { - field.classList.add("green") + field.classList.add('green') } field.onchange = handleChange ``` -Don't cringe, it's a simple component that turns green (an action) whenever a particular event happens (the field is modified). This is the component's **behaviour**. +Don't cringe, it's a simple component that turns green (an action) whenever a particular event happens (the field is modified). This is the component's **behaviour**. -However, in order for this code to support _anything_ other than turning it green when it's modified, it will need conditional logic. If the field should only turn green if the has any text, then the event handler needs an _if_ test, _and_ it needs to check the "current state" of the component. This is the beginning of the complexity creep. +However, in order for this code to support _anything_ other than turning it green when it's modified, it will need conditional logic. If the field should only turn green if the has any text, then the event handler needs an _if_ test, _and_ it needs to check the "current state" of the component. This is the beginning of the complexity creep. ```js function handleChange(e) { - if (field.value != "") { - field.classList.add("green") + if (field.value != '') { + field.classList.add('green') } } ``` @@ -53,41 +53,46 @@ As mentioned earlier, when implementing statecharts, the events are passed on to The simplest state machine (a simple form of a statechart) is shown below. It has the same behaviour as the example above: ```js -var currentState = "not_green"; +var currentState = 'not_green' var stateMachine = { - "not_green": () => field.classList.add("green") + not_green: () => field.classList.add('green'), } function handleChange(e) { stateMachine[currentState]() } ``` -Now this state machine is extremely simple, it only has one state and knows only one way to behave. But it is now a lot easier to make this component do more. +Now this state machine is extremely simple, it only has one state and knows only one way to behave. But it is now a lot easier to make this component do more. ```js var stateMachine = { - "not_green": () => { field.classList.add("green"); currentState = "green"; }, - "green": () => { field.classList.remove("green"); currentState = "not_green"; } + not_green: () => { + field.classList.add('green') + currentState = 'green' + }, + green: () => { + field.classList.remove('green') + currentState = 'not_green' + }, } ``` -By changing the state machine alone, we can now change the behaviour of the component. It now alternates between having the "green" class every time the field changes. - -We can introduce a _guard_ in order to prevent the event from having an effect. Let's extend it so that the field is green only when text has been added to it: +By changing the state machine alone, we can now change the behaviour of the component. It now alternates between having the "green" class every time the field changes. +We can introduce a _guard_ in order to prevent the event from having an effect. Let's extend it so that the field is green only when text has been added to it: ```js var stateMachine = { - "not_green": () => { - if (field.value == "") return; - currentState = "green"; - field.classList.add("green"); + not_green: () => { + if (field.value == '') return + currentState = 'green' + field.classList.add('green') + }, + green: () => { + if (field.value != '') return + currentState = 'not_green' + field.classList.remove('green') }, - "green": () => { - if (field.value != "") return; - currentState = "not_green"; - field.classList.remove("green"); - } } ``` @@ -96,33 +101,33 @@ var stateMachine = { This is a crude approximation of a state machine, but it _is_ a state machine, and has a lot of the moving parts of a statechart too: -* It accepts events, although it simply treats all events equal. Real statecharts have named events. -* It has several states, `green` and `not_green` -* It has an "active" state (`currentState`) -* It reacts differently depending on which "active state" state it's in -* It also reacts differently depending on "real world" information (`if (field.value == "") return;`) -* It changes the "current" state when it deals with an event -* It has side effects (known as actions, it adds and removes the `green` class) +- It accepts events, although it simply treats all events equal. Real statecharts have named events. +- It has several states, `green` and `not_green` +- It has an "active" state (`currentState`) +- It reacts differently depending on which "active state" state it's in +- It also reacts differently depending on "real world" information (`if (field.value == "") return;`) +- It changes the "current" state when it deals with an event +- It has side effects (known as actions, it adds and removes the `green` class) It has a few limitations too, though -* It is only a glorified enumeration with side effects. -* It's just _two states_ -* It only handles _one event_ -* It's extremely tightly coupled to the rest of the system (it talks directly to the DOM) -* It's difficult to extend with substates +- It is only a glorified enumeration with side effects. +- It's just _two states_ +- It only handles _one event_ +- It's extremely tightly coupled to the rest of the system (it talks directly to the DOM) +- It's difficult to extend with substates -While it is possible to "roll your own" state machine, it is likely not worth the effort. There are numerous edge cases that you need to consider, and if you're not careful, such a one-off state machine becomes more difficult to maintain than the original spaghetti code it was meant to displace. It is a bit like rolling your own date handling code; it works for the simplest of cases, but is quickly outgrown. +While it is possible to "roll your own" state machine, it is likely not worth the effort. There are numerous edge cases that you need to consider, and if you're not careful, such a one-off state machine becomes more difficult to maintain than the original spaghetti code it was meant to displace. It is a bit like rolling your own date handling code; it works for the simplest of cases, but is quickly outgrown. -The code shown above was introduced solely to describe how a state machine fits in, in the context of user interfaces. In order to show more advanced examples, it's necessary to avoid coding the inner workings of the state machine, and try to focus on the important parts, namely the different states, and which events causes the states to change. +The code shown above was introduced solely to describe how a state machine fits in, in the context of user interfaces. In order to show more advanced examples, it's necessary to avoid coding the inner workings of the state machine, and try to focus on the important parts, namely the different states, and which events causes the states to change. In order to help, we'll be introducing a statechart library ## Introducing XState -XState is a javascript library that essentially allows us to hide the inner workings of the state machine. You provide it with an object that _describes_ the state machine you're interested in, and XState returns a state machine that provides _pure functions_ that you can use to answer the question: "If I'm in _this_ state, and _this_ happens, _what_ should I do?" +XState is a JavaScript library that essentially allows us to hide the inner workings of the state machine. You provide it with an object that _describes_ the state machine you're interested in, and XState returns a state machine that provides _pure functions_ that you can use to answer the question: "If I'm in _this_ state, and _this_ happens, _what_ should I do?" -To give you a quick primer, we'll rewrite the green / not green state machine above using XState. +To give you a quick primer, we'll rewrite the green/not green state machine above using XState. First of all, we'll need a state machine, constructed by XState. @@ -146,34 +151,34 @@ const stateMachine = Machine({ } ``` -One thing to note is that in state machines, and statecharts, events are given explicit names. For our simple example I called the event `change`. +One thing to note is that in state machines, and statecharts, events are given explicit names. For our simple example I called the event `change`. -Now, this `stateMachine` variable provides a _pure functional_ interface to the state machine. This means that this state machine cannot and will not have side effects. Every time you use it, you tell it what the "current" state is, the event (what "happens"), and _it_ tells you what happened. +Now, this `stateMachine` variable provides a _pure functional_ interface to the state machine. This means that this state machine cannot and will not have side effects. Every time you use it, you tell it what the "current" state is, the event (what "happens"), and _it_ tells you what happened. We start off our state machine by asking the state machine what the "initial" state is: ```js -var currentState = stateMachine.initialState; +var currentState = stateMachine.initialState ``` -`currentState` is an XState _State_ instance, which has a _value_ which initially should be `"not_green"`. It represents _our_ state. +`currentState` is an XState _State_ instance, which has a _value_ which initially should be `"not_green"`. It represents _our_ state. You can then simulate what happens if you pass it the `change` event: ```js -currentState = statemachine.transition(currentState, "change"); +currentState = statemachine.transition(currentState, 'change') ``` -`currentState` will now describe the `green` state, and if you did it again, it would be the `not_green` state once again. What we have might seem like a pretty advanced boolean, but don't despair. It's time to hook this state machine up to our user interface. To start with, we'll let any change in the text field will trigger the "change" event. +`currentState` will now describe the `green` state, and if you did it again, it would be the `not_green` state once again. What we have might seem like a pretty advanced boolean, but don't despair. It's time to hook this state machine up to our user interface. To start with, we'll let any change in the text field will trigger the "change" event. -The first thing we need to do when handling the event is to trigger a state change, as shown above. The next thing is then to check which state we're now in, and respond accordingly. +The first thing we need to do when handling the event is to trigger a state change, as shown above. The next thing is then to check which state we're now in, and respond accordingly. ```js -if (currentState.value == "green") { - field.classList.add("green"); +if (currentState.value == 'green') { + field.classList.add('green') } -if (currentState.value == "not_green") { - field.classList.remove("green"); +if (currentState.value == 'not_green') { + field.classList.remove('green') } ``` @@ -189,7 +194,7 @@ If you're not used to thinking in state machines, it is usual to try to solve th First of all, we need to gather information about the world that we want the state machine to be able to inspect, a form of "extended state". For our example we want the state machine's behaviour to depend on the input value, or more specifically, the length of the value (or something else that constitutes validity). This _extra data_ is passed as the third parameter to `transition`: ```js -currentState = stateMachine.transition(currentState, 'change', field.value); +currentState = stateMachine.transition(currentState, 'change', field.value) ``` For simplicity, we're just passing the value of the field as the extended state, we could pass in the length or, even better, an object literal with room for more variables. @@ -218,24 +223,24 @@ The state machine as it stands is useful in its own right: the behaviour is isol There's one last thing that we ought to do, and that's implement actual side effects of a state machine. -When you have a state machine or statechart that "drives" your UI, it is quite common for the states in the statechart to (at least at the highest level) correspond to "modes" of the user interface. In our sample we have "green" and "not_green" as states, and we have an ugly _if_ test which checks _which_ state we're in, and performs some actions based on it (adds/removes a class). +When you have a state machine or statechart that "drives" your UI, it is quite common for the states in the statechart to (at least at the highest level) correspond to "modes" of the user interface. In our sample we have "green" and "not*green" as states, and we have an ugly \_if* test which checks _which_ state we're in, and performs some actions based on it (adds/removes a class). An easy simplification of this is to set the `class` of the element to the value of the state and be done with it. This is a common way of using the "current state" to effect changes to the user interface. That ugly set of if-tests can be reduced to a simple assignment: ```js -field.classList.value = currentState.value; +field.classList.value = currentState.value ``` The field now gets the class based on the current state of the state machine. If we introduce a new state in the state machine, it automatically becomes a class of the field, for better or worse. This has some nice benefits: -* If we introduce a new state, we don't need to write any code to deal with that new state. -* If we introduce a new state, we can easily introduce a new CSS class that describes what that state should look like. +- If we introduce a new state, we don't need to write any code to deal with that new state. +- If we introduce a new state, we can easily introduce a new CSS class that describes what that state should look like. ### "Actual" side effects -There are some side effects that cannot be done based on the class alone, such as making a HTTP request. Such long running things are in statechart terminology called "activities", and activities are started and stopped by way of _actions_. Let's make an activity that represents a HTTP request. That activity is _started_ and _stopped_ by way of two functions we'll define: `startHttpRequest` and `cancelHttpRequest`. +There are some side effects that cannot be done based on the class alone, such as making a HTTP request. Such long running things are in statechart terminology called "activities", and activities are started and stopped by way of _actions_. Let's make an activity that represents a HTTP request. That activity is _started_ and _stopped_ by way of two functions we'll define: `startHttpRequest` and `cancelHttpRequest`. To avoid having to talk to a real server, we're just going to use `setTimeout` to simulate a long running request: @@ -260,15 +265,16 @@ function resultsArrived(data) { // interesting stuff happens here } ``` + > TKTK update from pens -When `startHttpRequest` is called, it will call `resultsArrived` after 2 seconds, unless `cancelHttpRequest` is called first. It has an unfortunate, but deliberate behaviour that if you call the startHttpRequest many times, it will actually call resultsArrived many times. +When `startHttpRequest` is called, it will call `resultsArrived` after 2 seconds, unless `cancelHttpRequest` is called first. It has an unfortunate, but deliberate behaviour that if you call the `startHttpRequest` many times, it will actually call resultsArrived many times. ### Making use of our "HTTP client" -The point of this exercise is to show how _side effects_ are handled in the state machine. As mentioned earlier, the HTTP request is called an _activity_ and it is often the case that an activity is tied directly to _being in a state_. But instead of having an `if` test to check if we're in a particular state, such side effects are often better to make explicit in the state machine. This is done by defining _actions_. +The point of this exercise is to show how _side effects_ are handled in the state machine. As mentioned earlier, the HTTP request is called an _activity_ and it is often the case that an activity is tied directly to _being in a state_. But instead of having an `if` test to check if we're in a particular state, such side effects are often better to make explicit in the state machine. This is done by defining _actions_. -An action can happen as a consequence of _entering_ or _exiting_ any state. So if we want this HTTP request activity to happen in a particular state, we just specify entry and exit handlers to _start_ and _stop_ the activity in question: +An action can happen as a consequence of _entering_ or _exiting_ any state. So if we want this HTTP request activity to happen in a particular state, we just specify entry and exit handlers to _start_ and _stop_ the activity in question: ```js green: { @@ -280,14 +286,14 @@ green: { This declares that the `startHttpReqest` side effect should happen when the _green_ state is entered, and that `cancelHttpRequest` should happen when it is exited. -XState then provides these actions as a string array in our `currentState`. If we happen to enter the green state, `currentState.actions` will be `[ "startHttpRequest" ]`. We can harness this by calling the corresponding function: +XState then provides these actions as a string array in our `currentState`. If we happen to enter the green state, `currentState.actions` will be `[ "startHttpRequest" ]`. We can harness this by calling the corresponding function: ```js // TKTK should be handleEvent or something. function transition(event, data) { - currentState = stateMachine.transition(currentState, event, data); - field.classList.value = currentState.value; - currentState.actions.forEach(item => window[item]()); + currentState = stateMachine.transition(currentState, event, data) + field.classList.value = currentState.value + currentState.actions.forEach((item) => window[item]()) } ``` @@ -299,28 +305,28 @@ The result is now that whenever we enter the green state, the `startHttpRequest` ### Handling the results -We now have a situation where the HTTP request fires, and then two seconds later, we get some results. The `resultsArrived` function is called with some data. +We now have a situation where the HTTP request fires, and then two seconds later, we get some results. The `resultsArrived` function is called with some data. -In a non-statechart driven system, this `resultsArrived` function would typically immediately update the DOM and go about its business. However, in a statechart driven system, we get the function to only _tell the state machine_ about the fact that some data arrived. The state machine would then decide what to do. This is a _different_ event than typing text into a field, so we'll give it a new name. We'll call this the `results` event. +In a non-statechart driven system, this `resultsArrived` function would typically immediately update the DOM and go about its business. However, in a statechart driven system, we get the function to only _tell the state machine_ about the fact that some data arrived. The state machine would then decide what to do. This is a _different_ event than typing text into a field, so we'll give it a new name. We'll call this the `results` event. ```js -var results; +var results function resultsArrived(data) { - results = data; - currentState = stateMachine.transition(currentState, "results"); - field.classList.value = currentState.value; - currentState.actions.forEach(item => window[item]()); + results = data + currentState = stateMachine.transition(currentState, 'results') + field.classList.value = currentState.value + currentState.actions.forEach((item) => window[item]()) } ``` We now already have some code duplication in that we have two places where the field.classList is updated, so it's probably about time to extract this into its own function, a "stateful" wrapper around the XState state machine: ```js -var currentState; +var currentState function transition(event, data) { - currentState = stateMachine.transition(currentState, event, data); - field.classList.value = currentState.value; - currentState.actions.forEach(item => window[item]()); + currentState = stateMachine.transition(currentState, event, data) + field.classList.value = currentState.value + currentState.actions.forEach((item) => window[item]()) } ``` @@ -339,13 +345,13 @@ function resultsArrived(data) { Note that the state machine hasn't declared what should happen when the `results` event happens, so let's add that to the definition too: -``` js +```js on: { results: "not_green", change: ... ``` -This tiny change _handles_ the `results` event by telling the machine to go to the `not_green` state. With our fake HTTP request, you can see that the machine leaves the "green" state after 2 seconds. +This tiny change _handles_ the `results` event by telling the machine to go to the `not_green` state. With our fake HTTP request, you can see that the machine leaves the "green" state after 2 seconds.

See the Pen Green input box (xstate version 4, actual side effects) by Erik Mogensen (@mogsie) on CodePen.

@@ -354,38 +360,25 @@ This tiny change _handles_ the `results` event by telling the machine to go to t We now have a lot of the building blocks in order to make efficient use of statecharts. We have a machine which: -* Accepts events, and "guard" data -* Tells us what activities to start and stop -* Tells us "what state" it's in +- Accepts events, and "guard" data +- Tells us what activities to start and stop +- Tells us "what state" it's in But harnessing these building blocks is for another page, but to start you off with some exercises, you can try a few things: -* Introduce a new state ("red") and when you get results back from the "server", transition to it instead. Add some CSS for it too. -* Add a guard condition that checks the data coming back from the server, and go to the red or not_green states accordingly -* Go from the red state to the green/not_green states accordingly. - - - - - - - - - - - +- Introduce a new state ("red") and when you get results back from the "server", transition to it instead. Add some CSS for it too. +- Add a guard condition that checks the data coming back from the server, and go to the red or not_green states accordingly +- Go from the red state to the green/not_green states accordingly. TKTK stuff below this line can be ignored, I think it's food for a separate article. +- Actions should be used for side effects +- User interface changes could be deemed a side effect, so _can_ be controlled via actions +- The "current state" can be thought of as an implicit side effect +- It's possible to take the "current state" and control user interface changes based on it +Especially in declarative UI frameworks like HTML or React, it makes a lot of sense to model the statechart based on different "modes" of the UI, and use normal statechart mechanisms to control which is the "current state". It therefore makes a lot of sense to re-use the "current state" and pass this knowledge on to the declarative UI, basically asking the UI to render the "current state" UI. -* Actions should be used for side effects -* User interface changes could be deemed a side effect, so _can_ be controlled via actions -* The "current state" can be thought of as an implicit side effect -* It's possible to take the "current state" and control user interface changes based on it - -Especially in declarative UI frameworks like HTML or React, it makes a lot of sense to model the statechart based on different "modes" of the UI, and use normal statechart mechanisms to control which is the "current state". It therefore makes a lot of sense to re-use the "current state" and pass this knowledge on to the declarative UI, basically asking the UI to render the "current state" UI. - -This has the benefit of keeping the statechart very much in line with the major modes of the UI. When a component gets a new "mode of operation", it also gets a new "top level state". This makes it easier when showing e.g. a statechart to non-developers, like QA or designers, since they will quickly recognise the states and be able to relate to them. +This has the benefit of keeping the statechart very much in line with the major modes of the UI. When a component gets a new "mode of operation", it also gets a new "top level state". This makes it easier when showing e.g. a statechart to non-developers, like QA or designers, since they will quickly recognise the states and be able to relate to them. TKTK not finished yet.