Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
This article is about React-Machinery, a library for creating and using state machines in react to control UI. Find the full source on github
State is everywhere, and itâs an especially hot topic in the web development community. We have libraries like redux to help manage global state, but on the component level we can go further and harness some good old fashioned computer science.
Almost all UI components have a specific set of states they can be in. Simple components like dropdowns have unselected, in selection, and selected states. Complex views like a youtube video component have many states, like playing, paused, loadingâââand many substates like if the volume control is expanded, or if closed captions are enabled. This is not limited to UI eitherâ any kind of application, object or function can be stateful.
As developers we often model these states using boolean variables and if statements. While this is not bad per se, there are some problems with this approach:
- combinations of boolean expressions can be tricky to understand and communicate
- as requirements change, these branching if statements can start to produce possibly unwanted side effects
Ever had one of those bugs where for some reason, two completely different parts of state come together in an unexpected way to produce a weird effect? Iâve had this on almost every kind of project Iâve worked on, from games to SPAs to REST backends.
Fortunately computer science provides a very nice solution to this class of problems, aptly named Finite State Machines.
Finite State Machines > Combinational Logic
First of all, roughly speaking a finite state machine is:
A finite-state machine (FSM) or finite-state automaton (FSA, plural: automata), finite automaton, or simply a state machine, is a mathematical model of computation. It is an abstract machine that can be in exactly one of a finite number of states at any given time. The FSM can change from one state to another in response to some external inputs; the change from one state to another is called a transition. An FSM is defined by a list of its states, its initial state, and the conditions for each transition.Taken from https://en.wikipedia.org/wiki/Finite-state_machine
Hopefully that definition connects with what I talked about above with UI states. If not, donât worryâââa practical example is coming!
As a small note, itâs interesting to take note of that word automaton in the description. In math and computer science, an automaton is an âabstract machineâ which deals with computability. These automata form a hierarchy:
From: https://en.wikipedia.org/wiki/Automata_theory
As you can see from the diagram, finite state machines occupy a higher tier of automata than mere combinational logic (booleans and if statements). Simply put, they provide you the language to describe something stateful in a more precise way than you can with combinational logic alone.
We can put this theory into practice and use them with React âïž
The <HardChoiceButton> Component
A while back Wes Bos posted this tweet shortly after the Hawaiian missile scare:
body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
What are your favourite ways to handle scary button presses? I have a react component where I must click the button three times before I do any irrevocable operation https://t.co/4e2i623uA8
âââ@wesbos
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}
Iâm going to use this component as inspiration and build something similar using react-machinery.
The result will be a component called the HardChoiceButton, and it will take a message to display, a number of seconds to countdown, and an action function to run as props.
The button starts in a wait for input state, and when a user presses the button, they enter the pending state.
During the pending state a timer is ticking down from 5 (or whatever value we pass). At any time during this countdown, the user can press the button again to abort the operation, taking them to the aborted state.
If they donât press the button during pending, and the countdown makes it all the way to 0, then the action is executed and the button goes to the done state.
We can represent the above using logic with the following state diagram:
Letâs start defining the HardChoiceButton class:
As you can see, the StateMachine component exposed by react-machinery take 4 props: the states array, functions to get/set the current state, and some data properties which Iâll get to in a second. You can wire up whatever data source you want to the getCurrentState() and setNewState() functionsâââtaking data from a redux store or mobx state treeâââbut this example just uses regular component state.
The states array contains objects that have a name, and in this case a function called render. The render function here represents the component to render when in the wait-for-input state. This function receives the data object as props, which allows us to display the âLaunch Missilesâ message.
The HardChoiceButton is not much good if it only has a single state though! Letâs go ahead and fill in the rest of the states.
A small note before we do: components that are associated with each state can be given in two ways. The first is through a render function like above. This way follows the render prop pattern. The second way is to instead specify a component property. This is not a function, but rather just a normal component. This style is a little more applicable if you have a react class component with itâs own internal state.
Complete state definition
When we click the button in the wait-for-input state, we call transitionTo(âpendingâ). This is a special function that the StateMachine component passes to render along with the data object.
There is another key inside called validTransitions. This is used to register the states that are allowed to be used with transitionTo. Trying to transition to a state not registered will result in an error.
There is also a beforeRender function that allows you to perform some side-effecty stuffâââin this case making sure the timer is reset whenever we enter the wait-for-input state. This way we can ensure the timer always starts at 5.
When the pending state becomes active, it renders the Pending component, a fairly straightforward class component that creates and destroys and interval, and renders the new button. Every second, the interval calls the decreaseTimeLeft() function that was defined in the data prop of StateMachine.
The mechanism to transition state to the done state is a little different for pending though. Instead of calling transitionTo, we can describe an automatic transitionâââone that uses a function to determine whether to change state. These kind of transitions can be defined in states by the autoTransitions key.
The rest of the states just use combinations of these different kinds of transition logic.
Benefits
So what benefits can you expect modelling components this this way? For starters, you know for sure that you cannot arrive in a state that you did not explicitly define. Think about that for a minute because itâs a pretty big idea. Having a state machine like this essentially eliminates a whole class of bugs that you have to test for.
Building on top of that, because youâre explicitly defining this data structure, you can use it to generate information about your components. Take the following function:
That produces the following object (shown as JSON):
You can use this information to generate state diagrams for your component. Itâs literally self documenting.
Now imagine you have logging enabled on a production site. You can easily write a state machine that logs every state change, providing you with breadcrumbs back to the source of any error.
Lastly, changes to requirements in business logic can often be cleanly added. Letâs say we needed to add a new behaviour when the user clicks the button with 1 second left on the clock. Instead of simply going to the aborted state, we need to go to the that-was-close state.
Conclusion
If you find yourself developing bugs because of strange combinations of if-statements, considering implementing components using finite state machines instead. You can start using them in React right now with react-machinery (only 2kb gzipped!). State machines will help you model, reason about, and communicate the workings of your components more effectively.
If you found this interesting please leave a đ or two. Check out the code for React-Machinery on github and give me feedback there or on twitter @fstokesman.
Upgrade your React UI with state machines đĄ was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.