by Chris Aquino
How to build a dashboard for your grill using Arduino and React
“When should I use React?” My students at DigitalCrafts have been asking me this question a lot lately.
We’re three months into the bootcamp and two days into React. Several students have commented that React has a weird mix that combines the familiar with the foreign.
My favorite go-to answer is dashboards. React is great for creating panels that independently update information. It is the kind of UIs that is a nightmare to build using jQuery.
Then they ask, “Ok, like what?”
And then I show them…PitMaster.
First, a confession: I have an unnatural love for barbecue. If I’m not eating barbecue, I’m making a barbecue. (In the Southern United States, barbecue is a noun, not a verb. It is the process of exposing meat to low-temperature smoke for hours.)
Last Thanksgiving, in anticipation of being extremely lazy, I built a device that monitors the progress of turkey cooking in a smoker. The results were displayed in real time on a web page. Granted, a turkey does not take long to cook when compared to other kinds of meat. And I am really not that lazy, but I had a grander vision in mind.
Picture this: you’re 12 hours into an 18-hour smoke, and you’d really rather not get off the couch to check the temperature yet again. Besides, you shouldn’t open the smoker during the cook, thereby causing a massive temperature fluctuation.
The details of the hardware could fill another blog post, but here is a rough sketch of it.
Poke a food-safe, high-temperature thermal resistor (thermistor) into the food just before you place it into the smoker. A thermistor is a piece of hardware that changes its electrical conductivity as its temperature changes. A long wire runs from the thermistor to an Arduino.
The Arduino runs a program that converts the analog reading from the thermistor to a digital value. A Raspberry Pi polls the Arduino for this value every few 100 milliseconds, logs it to a file, and then pushes it to any client connected via WebSocket.
It may sound fancy, but here’s what the contraption actually looks like.
Originally, the UI for displaying the current temperature was, to put it plainly, really ugly. It was precisely what you’d see in a “Hello World” tutorial, consisting of a single unstyled
h1 in all its Times New Roman glory.
So I built
PitMaster , a dashboard that monitors the progress of smokers. You can grab a copy of the code here.
In this post, I'll go over some key concepts that eluded me when I started learning React.
Despite all the buzz about “functional, declarative UIs” and “diffing the Virtual DOM,” I focus on these three guidelines when building with React:
- Data in, UI out
- Build your components bottom-up, and pass data top-down
- Make your components as dumb as possible, but no dumber
With these in mind, let’s look at the UI.
Sketching out PitMaster
Here is a basic wireframe of the PitMaster UI:
As recommended in the excellent Thinking in React page, the best way to start is by doing a visual breakdown of your UI.
PitMaster is an application split into two parts: a form that can be used to add orders and a panel that displays the current status of each order.
The form can be broken down into three parts:
- Drop-down with the different types of foods
- Text input to enter the name of the person who is ordering the food
- Submit button to submit the form
The panel only holds an individual “monitor” for a food order. These appear to be more complex, but they don’t do much other than display text and numbers. We’ll start with these, as they demonstrate the most fundamental idea in React: transforming data into an UI.
Using functions to transform data into UI components
React lends itself perfectly to atomic design principles. This has nothing to do with nuclear physics and everything to do with a recent movement in UI design. In atomic design, you start by designing the smallest visual components and then composing them into larger and larger ones. This gives you the opportunity to reuse components in other parts of your UI. In the end, it’s less work and creates consistency across your entire UI.
In PitMaster, you can see that the temperature Readout is the smallest, so I’ll start there. Another good candidate is the NameLabel, but the Readout is reused more often.
Building the Readout component
Component in React is something that accepts data and returns a description of the UI that displays that data. Functions are exactly the right tool for the job. The
Readout component will be no more than a function that accepts text, numbers as arguments, and returns a
<span> with those values inside.
Here is the simplest version of that:
There are two things to note about this.
First, any data that is passed to a component is bundled up in an object called
value must be accessed as properties of the
Second, you aren’t actually creating DOM elements, you’re creating React elements, which are descriptions of DOM elements. This is going to feel very different if you are used to building UIs with jQuery, where you are either creating DOM elements or referencing existing DOM elements. The reasons for this will become more apparent soon.
React.createElement('span', null, props.label, props.value) means that the type of DOM element is a
We are passing no
props to it, and nested inside
span are the values
Values that are nested inside of a React element are referred to as children.
Arrow functions and destructuring
There is an alternative syntax for functions that is used heavily in many React codebases. ES6 arrow functions are the same as anonymous functions, but with shorter syntax. (Because programmers hate pressing more keys than they need to!)
In the next snippet of code, an arrow function value is assigned to the variable
Readout, which is declared using
const is a variable that cannot be reassigned.
But wait, there’s more!
We can use ES6 destructuring syntax to pull specific values out of the
props argument and assign them to local variables, all in one step.
React requires that your components return a single element. With arrow functions, if the body only has a single statement and it’s a
return statement, then you can omit the curly braces and the
return keyword. The value to the right of the arrow is implicitly
return value can get long, you'll see code that looks like this, where parentheses have been added around the implicit return value:
It’s the same thing, but it lets the programmer do what they will with whitespace.
There’s another abstraction to add. Maybe we’ll need to put
span inside of a
div for styling purposes:
As you can imagine, as we try to describe more complex DOM structures, the nested
React.createElement calls can quickly get out of hand. Thankfully, React lets you use an optional syntax for
React.createElement that looks a lot like HTML. Here's that component rewritten using that syntax:
The advantage here is that anyone familiar with HTML can glance at the JSX and know what DOM elements will be produced.
What’s handy about
React.createElement is that you aren't limited to describing HTML elements. Once you create a component like
Readout, you can use it with
React.createElement or with JSX.
Here's the full code listing for the component that displays three recent temperatures from three different time intervals (1 minute, 5 minutes, and 10 minutes).
You can mix your custom components in with the native HTML ones. Remember, your
Readout components produce elements that are a
span inside of a
And to pass arguments, for example
props,to a custom component, you can use syntax that looks like HTML attributes:
If you look at
Monitor.js, you can see that it is a combination of
NameLabel, and HTML components:
By now, it should be clear that components are nothing more than shorthand for nested calls to
React.createElement. Each component receives data (
props) from its parent and passes data to its children. All of the data that is displayed in a React app was originally passed to the root, top-most element.
But the question then becomes, what is the result of all these function calls?
Demystifying the Virtual DOM
The answer is that all these nested
<TemperatureHistory valueArray=[154, 132, 126] />:
React uses the element tree to create actual DOM elements and render them to the page. This is what is meant by the term Virtual DOM. The element tree serves as a blueprint for the DOM, and React can refer to this as it needs to update the page.
Updating the DOM
Alright, some time passes and the barbecue progresses towards delicious goodness. As the temperature readings change, so does the UI. Let’s say that only the first value of the temperature history has changed (from 154 to 156):
<TemperatureHistory valueArray=[156, 132, 126] />
When the data changes, the new values are passed to the root component, and the data cascades down the component tree. This results in a new element tree.
This is the resulting element tree:
React then does something very clever. It compares this new element tree to the previous one. It then determines the minimal number of places in the DOM that it has to update. It literally figures out the difference between the two trees in order to do the least amount of work to ensure that the DOM reflects the most recent version of the data. (You should be freaking out right now because that’s really amazing.)
The next question is if everything in the element tree is a static value, how and where do we keep up with the application data?
A good example of a component that needs to keep up with application data is the
FoodChooserForm. As the user enters information into the form, the data in the form changes, and this technically causes the form elements to re-render. But we need to make sure that anything the user has entered so far is retained.
Adding class components
Up till now, we have only looked at components that are functions. More specifically, these are known as stateless functional components. Most of your components will be of this variety. There is another kind of component that has a few additional capabilities. To create them, we’ll need to use the ES6 class syntax.
FoodChooserForm, showing everything except the element tree it produces.
FoodChooserForm is an example of a stateful class component.
It inherits from
React.Component and invokes
super(props) in its
constructor. Also, notice that it initializes the instance variable
state variable is where a class component stores its application data. It has two other methods
_updateFoodChoice. Each of these methods calls
this.setState, which is a method provided by
React.Component. This is the method that class components must use to make changes to the value of
this.state. You pass it an object with the key/value pairs to update. React takes care of the rest.
Here is the full component code. It includes the definition of the
render method, which is equivalent to the return value of a stateless functional component.
_handleSubmit are all prefixed with an underscore. This is a common convention that distinguishes private methods from inherited methods. These private methods are passed as
props to the event handlers of the components listed in the
render method. Those components will receive these functions and blindly call them when those events are triggered, thus causing a change to the
This is how most of your components can be “dumb” while a select few can be in charge of maintaining the state. Your dumb components know just enough to call these functions and pass to them the right information. In this case,
_updateFoodChoice whenever something is selected from its drop-down menu, and
_updateOrderName as the user types in the text field.
FoodChooserForm receives the most current value for the food choice and the order name, and saves it via
FoodChooserForm also invokes a function that it was passed as props. In the constructor (where it receives all of its props), it saves a reference to
props.submitHandler as the instance variable
this.submitHandler. In the
render method, it passes a reference to its
_handleSubmit method to the
form's submit event is triggered,
_handleSubmit is called and the event information is passed to it.
FoodChooserForm prevents the
form from submitting (which would cause the page to reload). Then, it calls the
submitHandler, passing it the current value of
this.state. Finally, it resets value of its
state, effectively resetting the form by blanking out the rendered values.
And where does that new food order information now go? All the way to the top.
The PitMaster component
PitMaster maintains a list of all food orders as part of its
state, which it passes to the
MonitorPanel. It provides
FoodChooserForm with a list of food choices and a callback function to use whenever a new order has been placed. Its
render method is refreshingly brief:
It has private methods for adding and removing orders, as well as updating the list of temperatures. All other behaviours have been tucked away in other components.
My DigitalCrafts students are breathing a little easier as they bravely tackle yet another new topic in web development. They know that they can still use jQuery for smaller jobs, build a back-end with Postgres and Express for a server-rendered website, or make use of React for component-based single page apps. Most importantly, they’re able to take those fundamentals they have put many hours into, and apply them to whatever shiny new thing comes next.
Now, I wonder what they’ll ask me when we take on Redux next week…