Datavis 2020 Episode 8 – Let’s Make a Face Part IV (React Components & ES6)

We’ll discuss:
ES6 Background
ES6 Arrow Functions
JSX Transpilation
ES6 Variables with let and const
ES6 Destructuring

Intro to ES6

ES6 stands for ECMAScript version 6. ECMAScript is another name for JavaScript. You’ll find that there are various versions – fourth edition, fifth edition, sixth edition. The fifth edition is what I consider to be old-school JavaScript. This is the kind of JavaScript that you’d write since 2009 – between 2009 and 2015. The JavaScript that you see is ES5. 

var square = function (x) { return x * x; }

This is ES5 JavaScript. You use var to declare variables, and you need to use the return statement, and you always need to use curly braces with functions. So if we define that, and then we call square(4) of it returns 16

In Chrome, there are many ways to open the JavaScript console –  you can click on these three dots, and then go under More tools and select Developer tools. You can also open with Ctrl + Shift + I. You can type any JavaScript in here, in the console tab, and hit Enter. console.log(“Hello JavaScript”) 

Any errors that occur in the files that you edit will also show up here in the console. So it’s very useful for debugging. 

The 6th edition is ECMAScript version 6, also abbreviated ES6, and it’s also called ECMAScript 2015 or ES 2015. One of the biggest improvements in this version is ES6 modules. And that’s what lets you write this import * as modulename syntax. Other changes include using const and let to define variables instead of var. And also this really sweet syntax for functions, namely the arrow syntax. ()=> {…}

ES6 Arrow Functions

const square =  (x) => {return x * x}

const square equals a function where x is the argument, and the return value is x times x

This is what you would write in ES6. You can leave out these parentheses.

const square =  x => {return x * x}

You can leave out these curly braces, and if you do, you can leave out this return statement. 

const square =  x => x * x

This implicitly returns whatever the expression is.

If you have a large expression that you want to return, you can put it in parenthesis, and this is common practice with JSX, with React.

const square =  x => (x * x)

But anyway this is how I would write square in ES6.

const square =  x => x * x

It does the same thing as the other ones. We can invoke it  – square(4) and it returns 16

JSX Transpilation

Back in our code, here I’m going to start refactoring this to use React components. And by the way, we already have one React component, and it’s defined as an ES6 arrow function that returns some JSX. And by the way, when this JSX gets transpiled, because JSX is not part of JavaScript, we can take a look at the generated bundle.js to see how it gets transpiled from JSX into ES6.

See here’s our App component. And this is actually the code that runs React. All of our attribute values get passed in like this. So I just want you to be aware that JSX doesn’t actually run. It gets transpiled into this, and then gets run. So what this (const App) is assigned to, is a function that returns an object returned from React.createElement. This is called virtual DOM. Virtual Document Object Model. And React takes care of making sure that the actual DOM on the page mirrors your intended DOM. The virtual DOM that gets returned by this function. 

But anyway, back in index.js, let me start refactoring. 

We’ve got all of our logic sort of jumbled up inside of index.js. We’ve got variables that are global to this module, and we’re not really using the component infrastructure that React provides. Just looking at this, I have no idea what each of these different things represents. I would much rather see eyes, mouth components. Names that have some kind of semantic meaning. And usually, the app component sort of orchestrates all the other components. It doesn’t directly render these DOM elements like this. So I’m gonna go ahead and start refactoring this to take advantage of components and modules.

The first thing that stands out is this circle when I just glance at the code I have no idea what this represents. 

<circle
  r={centerY - strokeWidth / 2}
  fill="yellow"
  stroke="black"
  stroke-width={strokeWidth}
/>;

What I would prefer to do is say background circle perhaps? </BackgroundCircle/>

And this is what it looks like to use a React component. It needs to be uppercase, because if it’s lowercase, then React JSX sort of interprets it as a native kind of a DOM element. But if it’s uppercase, it should be a defined component that you have. So let me define the component now. Say const BackgroundCircle equals a function that returns in parentheses, the JSX that I had cut from before. There we have it. We have defined a React component and used it.

const BackgroundCircle = () => (
  <circle
    r={centerY - strokeWidth / 2}
    fill="yellow"
    stroke="black"
    stroke-width={strokeWidth}
  />
);

One thing that doesn’t quite feel right about this component is that it refers to these variables that are defined in this scope here at the start of index.js

In order to make this component independent, we can pass in these things as props – React props or properties. I’d like to make it the responsibility of the outer component to calculate the radius and then pass it into BackgroundCircle.

So I’m going to cut this code here, and put radius instead. And we can get radius from props, and we can access it like this – props.radius

const BackgroundCircle = (props) => (
  <circle
    r={props.radius}
    fill="yellow"
    stroke="black"
    stroke-width={strokeWidth}
  />
);

This is currently broken because we’re not passing in any radius. Let’s pass in the value for radius to our BackgroundCircle component. And that can look like this.. radius= and then, in curly braces, we can put any JavaScript expression. And here, I’m going to paste that expression that I had from earlier.

<BackgroundCircle radius={centerY - strokeWidth / 2} />

And Boom! It works. This is how you define React props – radius is a prop that’s being defined as this value – centerY - strokeWidth / 2, and then we can access it as props.radius inside of this function assigned to const BackgroundCircle.

ES6 Destructuring

This could be simplified though by using ES6 destructuring, which looks like this. 

const BackgroundCircle = ({ radius }) => (
    <circle
      r={radius}
      fill="yellow"
      stroke="black"
      stroke-width={strokeWidth}
    />
);

To make sure this is crystal clear, let me just show some examples of this. 

const person = {
  firstName: 'Jane',
  lastName: 'Doe',
};

Let’s declare an object called personfirstName Jane, and lastName Doe. So we define this object, and then we can access person.firstName and person.lastName

If we want first name and last name available to us as sort of local variables, we could extract them like this.

const firstName =  person.firstName
const lastName = person.lastName. 

And now we have firstName and lastName defined. So we can just confirm that by saying, console.log(firstName, lastName)

The problem here is that this is kind of verbose, and we see that verbosity in a React context, with props.this, props.that, we don’t want props.this and that all over the place. One way to solve that is put all the props. in one place like this. But another, cleaner solution is to use ES6 destructuring. And that looks something like this. 

const { firstName, lastName } = person

ES6 Variables with let and const

This does the exact same thing as these two lines together. If I run this I get an error, because I’m using const. When you use const you can only define these things once. They can’t be mutated. They can’t be changed after the fact. See, if I say const foo=5, and then I try to say foo=6, it doesn’t work. But if you want to change something, that’s where you need to use letlet bar=5

Since we used let to declare it, we can say bar=6. No problem. So there’s a little detour explaining the background of these ES6 language features, and this is why they’re useful in refactoring React components. 

import React from 'react';
import ReactDOM from 'react-dom';
import { arc } from 'd3';

const width = 960;
const height = 500;
const centerX = width / 2;
const centerY = height / 2;
const strokeWidth = 20;
const eyeOffsetX = 90;
const eyeOffsetY = 100;
const eyeRadius = 40;
const mouthWidth = 20;
const mouthRadius = 140;

const mouthArc = arc()
  .innerRadius(mouthRadius)
  .outerRadius(mouthRadius + mouthWidth)
  .startAngle(Math.PI / 2)
  .endAngle((Math.PI * 3) / 2);

const BackgroundCircle = ({ radius }) => (
  <circle r={radius} fill="yellow" stroke="black" stroke-width={strokeWidth} />
);

const App = () => (
  <svg width={width} height={height}>
    <g transform={`translate(${centerX},${centerY})`}>
      <BackgroundCircle radius={centerY - strokeWidth / 2} />
      <circle cx={-eyeOffsetX} cy={-eyeOffsetY} r={eyeRadius} />
      <circle cx={eyeOffsetX} cy={-eyeOffsetY} r={eyeRadius} />
      <path d={mouthArc()} />
    </g>
  </svg>
);

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

Next up in Datavis 2020: Episode 9 – Let’s Make a Face Part V multiple files with ES6 modules