Learning Gutenberg: Modern JavaScript Syntax

0
14

One of the key changes that Gutenberg brings to the WordPress ecosystem is a heavy reliance on JavaScript. Helpfully, the WordPress team have really pushed their JavaScript framework into the present and future by leveraging the modern JavaScript stack, which is commonly referred to as ES6 in the community. It’s how we’ll refer to it as in this series too, to avoid confusion.

Let’s dig into this ES6 world a bit, as it’s ultimately going to help us understand how to structure and build a custom Gutenberg block.

Article Series:

  1. Series Introduction
  2. What is Gutenberg, Anyway?
  3. A Primer with create-guten-block
  4. Modern JavaScript Syntax (This Post)
  5. React 101
  6. Setting up a Custom webpack (Coming Soon!)
  7. A Custom “Card” Block (Coming Soon!)

What is ES6?

ES6 is short for “EcmaScript 6” which is the 6th edition of EcmaScript. It’s official name is ES2015, which you may have also seen around. EcmaScript has since gone through many iterations, but modern JavaScript is still often referred to as ES6. As you probably guessed, the iterations have continued ES2016, ES2017 and so-on. I actually asked a question on ShopTalk show about what we could name modern JavaScript, which I the conclusion was… ES6.

I’m going to run through some key features of ES6 that are useful in the context of Gutenberg.

Functions

Functions get a heck of an update with ES6. Two changes I want to focus on are Arrow Functions and Class Methods.

Inside a class you don’t actually need to write the word function anymore in ES6. This can be confusing, so check out this example:

class Foo {
// This is your ‘bar’ function
bar() {
return ‘hello’;
}
}

You’d invoke bar() like this:

const fooInstance = new Foo();
const hi = fooInstance.bar();

This is commonplace in the land of modern JavaScript, so it’s good to clear it up.

Fun fact! ES6 Classes in JavaScript aren’t really “classes” in an object-oriented programming sense—under the hood, it’s the same old prototypical inheritance JavaScript has always had. Prior to ES6, the bar() method would be defined like so: Foo.prototype.bar = function() { … }. React makes great use of ES6 classes, but it’s worth noting that ES6 classes are essentially syntactic sugar and hotly contested by some. If you’re interested in more details, checkout the MDN docs and this article on 2ality.

Right, let’s move on to arrow functions. 🚀

An arrow function gives us a compact syntax that is often used as a one-liner for expressions. It’s also used to maintain the value of this, as an arrow function won’t rebind this like setInterval or an event handler would usually do.

An example of an arrow function as an expression is as follows:

// Define an array of fruit objects
const fruit = [
{
name: ‘Apple’,
color: ‘red’
},
{
name: ‘Banana’,
color: ‘yellow’
},
{
name: ‘Pear’,
color: ‘green’
}
];

// Select only red fruit from that collection
const redFruit = fruit.filter(fruitItem => fruitItem.color === ‘red’);

// Output should be something like Object { name: “Apple”, color: “red” }
console.log(redFruit[0]);

As you can see above, because there was a single parameter and the function was being used as an expression, we can redact the brackets and parenthesis. This allows us to really compact our code and improve readability.

Let’s take a look at how we can use an arrow function as an event handler in our Foo class from before:

class Foo {

// This is your ‘bar’ function
bar() {
let buttonInstance = document.querySelector(‘button’);

buttonInstance.addEventListener(‘click’, evt => {
console.log(this);
});
}
}

// Run the handler
const fooInstance = new Foo();
fooInstance.bar();

When the button is clicked, the output should be Foo { }, because this is the instance of Foo. If we were to replace that example with the following:

class Foo {

// This is your ‘bar’ function
bar() {
let buttonInstance = document.querySelector(‘button’);

buttonInstance.addEventListener(‘click’, function(evt) {
console.log(this);
});
}
}

// Run the handler
const fooInstance = new Foo();
fooInstance.bar();

When the button is clicked, the output would be <button> because the function has bound this to be the <button> that was clicked.

You can read more about arrow functions with Wes Bos, who wrote an excellent article about them.

const, let, and var

You may have noticed that I’ve been using const and let in the above examples. These are also a part of ES6 and I’ll quickly explain what each one does.

If a value is absolutely constant and won’t change through re-assignment, or be re-declared, use a const. This would commonly be used when importing something or declaring non-changing properties such as a collection of DOM elements.

If you have a variable that you want to only be accessible in the block it was defined in, then use a let. This can be confusing to understand, so check out this little example:

function foo() {
if (1 < 2) {
let bar = ‘always true’;

// Outputs: ‘always true’
console.log(bar);
}

// Outputs ‘ReferenceError: bar is not defined’
console.log(bar);
}

// Run the function so we can see our logs
foo();

This is a great way to keep control of your variables and make them disposable, in a sense.

Lastly, var is the same old friend we know and love so well. Unfortunately, between const and let, our friend is becoming more and more redundant as time goes on. Using var is totally acceptable though, so don’t be disheartened—you just won’t see it much in the rest of this tutorial!

Destructuring assignment

Destructuring allows you to extract object keys at the point where you assign them to your local variable. So, say you’ve got this object:

const foo = {
people: [
{
name: ‘Bar’,
age: 30
},
{
name: ‘Baz’,
age: 28
}
],
anotherKey: ‘some stuff’,
heyAFunction() {
return ‘Watermelons are really refreshing in the summer’
}
};

Traditionally, you’d extract people with foo.people. With destructuring, you can do this:

let { people } = foo;

That pulls the people array out of the the foo object, so we can dump the foo. prefix and use it as it is: people. It also means that anotherKey and heyAFunction are ignored, because we don’t need them right now. This is great when you’re working with big complex objects where being able to selectively pick stuff out is really useful.

You can also make use of destructuring to break up an object into local variables to increase code readability. Let’s update the above snippet:

let { people } = foo;
let { heyAFunction } = foo;

Now we’ve got those two separate elements from the same object, while still ignoring anotherKey. If you ran console.log(people), it’d show itself an array and if you ran console.log(heyAFunction), you guessed it, it’d show itself as a function.

JSX

Most commonly found in React JS contexts: JSX is an XML-like extension to JavaScript that is designed to be compiled by preprocessors into normal JavaScript code. Essentially, it enables us to write HTML(ish) code within JavaScript, as long as we’re preprocessing it. It’s usually associated with a framework like React JS, but it’s also used for Gutenberg block development.

Let’s kick off with an example:

const hello = <h1 className=”heading”>Hello, Pal</h1>;

Pretty cool, huh? No templating system or escaping or concatenating required. As long as you return a single element, which can have many children, you’re all good. So let’s show something a touch more complex, with a React render function:

class MyComponent extends React.Component {
/* Other parts redacted for brevity */

render() {
return (
<article>
<h2 className=”heading”>{ this.props.heading }</h2>
<p className=”lead”>{ this.props.summary }</p>
</article>
);
}
};

You can see above that we can drop expressions in wherever we want. This is also the case with element attributes, so we can have something like this:

<h2 className={ this.props.headingClass }>
{ this.props.heading }
</h2>

You might be thinking, “What are these random braces doing?”

The answer is that this is an expression, which you will see a ton of in JSX. Essentially, it’s a little inline execution of JavaScript that behaves very much like a PHP echo does.

You’ll also probably notice that it says className instead of class. Although it looks like HTML/XML, it’s still JavaScript, so reserved words naturally are avoided. Attributes are camel-cased too, so keep and eye out for that. Here’s a useful answer to why it’s like this.

JSX is really powerful as you’ll see while this series progresses. It’s a great tool in our stack and really useful to understand in general.

I like to think of JSX as made up-tag names that are actually just function calls. Pick out any of the made-up tags you see in Gutenberg, let’s use <InspectorControls /> for example, and do a “Find in Folder” for class InspectorControls and you’ll see something structured like Andy’s example here! If you don’t find it, then the JSX must be registered as functional component, and should turn up by searching for function InspectorControls.

Wrapping up

We’ve had a quick run through some of the useful features of ES6. There’s a ton more to learn, but I wanted to focus your attention on the stuff we’ll be using in this tutorial series. I’d strongly recommend your further your learning with Wes Bos’ courses, JavaScript 30 and ES6.io.

Next up, we’re going to build a mini React component!

Article Series:

  1. Series Introduction
  2. What is Gutenberg, Anyway?
  3. A Primer with create-guten-block
  4. Modern JavaScript Syntax (This Post)
  5. React 101
  6. Setting up a Custom webpack (Coming Soon!)
  7. A Custom “Card” Block (Coming Soon!)