Tag Archives: JavaScript

JavaScript is the programming language of the Web and all modern HTML pages are using it

Gutenberg Element Read-Me

Share

Element

Element is, quite simply, an abstraction layer atop React.

You may find yourself asking, “Why an abstraction layer?”. For a few reasons:

  • In many applications, especially those extended by a rich plugin ecosystem as is the case with WordPress, it’s wise to create interfaces to underlying third-party code. The thinking is that if ever a need arises to change or even replace the underlying implementation, it can be done without catastrophic rippling effects to dependent code, so long as the interface stays the same.
  • It provides a mechanism to shield implementers by omitting features with uncertain futures (createClass, PropTypes).
  • It helps avoid incompatibilities between versions by ensuring that every plugin operates on a single centralized version of the code.

On the wp.element global object, you will find the following, ordered roughly by the likelihood you’ll encounter it in your code:

Installation

Install the module

npm install @wordpress/element --save

This package assumes that your code will run in an ES2015+ environment. If you’re using an environment that has limited or no support for ES2015+ such as lower versions of IE then using core-js or @babel/polyfill will add support for these methods. Learn more about it in Babel docs.

Usage

Let’s render a customized greeting into an empty element:

<div id="greeting"></div>
<script>
function Greeting( props ) {
    return wp.element.createElement( 'span', null, 
        'Hello ' + props.toWhom + '!'
    );
}

wp.element.render(
    wp.element.createElement( Greeting, { toWhom: 'World' } ),
    document.getElementById( 'greeting' )
);
</script>

Refer to the official React Quick Start guide for a more thorough walkthrough, in most cases substituting React and ReactDOM with wp.element in code examples.

Why React?

At the risk of igniting debate surrounding any single “best” front-end framework, the choice to use any tool should be motivated specifically to serve the requirements of the system. In modeling the concept of a block, we observe the following technical requirements:

  • An understanding of a block in terms of its underlying values (in the random image example, a category)
  • A means to describe the UI of a block given these values

At its most basic, React provides a simple input / output mechanism. Given a set of inputs (“props”), a developer describes the output to be shown on the page. This is most elegantly observed in its function components. React serves the role of reconciling the desired output with the current state of the page.

The offerings of any framework necessarily become more complex as these requirements increase; many front-end frameworks prescribe ideas around page routing, retrieving and updating data, and managing layout. React is not immune to this, but the introduced complexity is rarely caused by React itself, but instead managing an arrangement of supporting tools. By moving these concerns out of sight to the internals of the system (WordPress core code), we can minimize the responsibilities of plugin authors to a small, clear set of touch points.

JSX

While not at all a requirement to use React, JSX is a recommended syntax extension to compose elements more expressively. Through a build process, JSX is converted back to the createElement syntax you see earlier in this document.

If you’ve configured Babel for your project, you can opt in to JSX syntax by specifying the pragma option of the transform-react-jsx plugin in your .babelrc configuration.

{
    "plugins": [
        [ "transform-react-jsx", {
            "pragma": "createElement"
        } ]
    ]
}

This assumes that you will import the createElement function in any file where you use JSX. Alternatively, consider using the @wordpress/babel-plugin-import-jsx-pragma Babel plugin to automate the import of this function.

Code is Poetry.

Share

Iterator Example

Share

Iterator Example

This is a poor example as you could certainly accomplish this more easily with .map(), however, this demonstrates that the iterator ‘remembers’ its position and picks up where it left off. A better example would be where other procedures or logic are executed between calls.

function makeIterator(array) {
    var nextIndex = 0;
    return {
        next: ()=>(nextIndex < array.length) && array[nextIndex++],
        remain: ()=>array.length - nextIndex
    }
}
var fun = makeIterator(['fe', 'fi', 'fo', 'fum', 'I', 'smell']);

while(fun.remain()){
    console.log(fun.next());
}
Share

Array Iteration

Share

Two quick ways to iterate through an array (from .length to 0).

What’s the point?

  • It’s fast to compare something to zero
  • Less code overall

Why it’s weird…

We’re used to counting from zero to length – not vice-versa

When not to use

The only place I can think of that this wouldn’t be a good idea would be when you need to iterate over the array in ascending key order.

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
var b = a.length;

// quickly iterate through array
while (b--) {
    console.log(a[b]);
}

// declaring var in same line
for (var c = a.length; c--; ) {
    console.log(a[c]);
}

// of course THIS is best
a.forEach(i => console.log(i));
Share

Thoughts on coercion

Share

Thoughts on coercion

Here’s an interesting tidbit. If you’ve ever used indexOf to determine if a string contains another string (perhaps I could word this better…), then you’ve probably used something like:

var a = 'Jim';
a.indexOf('i') !== -1; // true

As you’re probably aware, if the string is not found, –1 is the return value.

A simpler and more efficient way to accomplish this test is:

var a = 'Jim';
~a.indexOf('i'); // true (coerced)

or

Boolean(~a.indexOf('i')); // true

A lot of people will state that coercion in JS is evil. I’d state that what is really evil is when people don’t know what that coercion is doing. I recently watched a video series by Kyle Simpson where he illustrated a few places where using coercion to your advantage will create simpler, easier to understand, less error-prone code. For example:

var foo; // undefined

Let’s say that foo, in this case could be undefined or null.
Using explicit coercion, our check for this value would be:

if ( foo===undefined || foo===null ) {
    // should work, all bases are covered
}

A simpler, more readable check:

if ( foo == null ) {
    // works for null or undefined
}
Share

Partially-Applied Function – Practical Example

Share

Partially-Applied Function – Practical Example

This example demonstrates several concepts:

  • modules
  • partially applied functions
  • functional programming techniques

The partialGetElID function creates a partially applied function that replaces hyphenated, underscored, or dotted names with their camelCased variant.

module.exports = {
    partialGetElID: (wf, pg) =>el=>`${sepsToCamelCase(wf)}-${sepsToCamelCase(pg)}-${sepsToCamelCase(el)}`
};

var sepsToCamelCase = function(arg) {

    var upperFirst = s => s.replace(s.charAt(0), s.charAt(0).toUpperCase());
    var lowerFirst = s => s.replace(s.charAt(0), s.charAt(0).toLowerCase());

    return (
        R.pipe(
            R.split(/[._-]/g),
            R.map(upperFirst),
            R.join(''),
            lowerFirst
        )(arg)
    );
};

Usage:


var X = require('path/to/module');
var getID = X.partialGetElId('workflow.name', 'step_name');

getID('element-name') // returns workflowName-stepName-elementName
Share

Functional Programming Example – using Ramda

Share

Functional Programming Example – using Ramda

Copy and paste this code into your console. It automatically loads Ramda and uses that to process the beerData.

Two concepts covered in this example:

  • Loading external scripts into the console (which works in chrome developer tools snippets at minimum). I can’t seem to find a way to make it work in firefox.
  • Functional programming.

#functional, #loadingScripts

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '//cdnjs.cloudflare.com/ajax/libs/ramda/0.21.0/ramda.min.js';
document.head.appendChild(script);

beerData = [
    {"name": "Bitter Hop", "type": "ipa", "locale": "domestic", "abv": 7.9},
    {"name": "Dragonblood Black IPA", "type": "ipa", "locale": "domestic", "abv": 6.5},
    {"name": "Good Dog Lager", "type": "lager", "locale": "domestic", "abv": 4.8},
    {"name": "Good Dog Snowpants", "type": "stout", "locale": "domestic", "abv": 8.1},
    {"name": "Lake Erie 2x IPA", "type": "ipa", "locale": "domestic", "abv": 9.3},
    {"name": "Lake Erie Session", "type": "ale", "locale": "domestic", "abv": 4.2},
    {"name": "Samwell British Pale Ale", "type": "ale", "locale": "import", "abv": 6.5},
    {"name": "Samwell Oatmeal Stout", "type": "stout", "locale": "import", "abv": 5.5},
    {"name": "Samwell Winter Ale", "type": "ale", "locale": "import", "abv": 6.8},
    {"name": "Sparkwood Beer Five", "type": "lager", "locale": "domestic", "abv": 6.2},
    {"name": "Sparkwood Campfire Lager", "type": "lager", "locale": "domestic", "abv": 5.8},
    {"name": "Tartan Scottish Ale", "type": "ale", "locale": "import", "abv": 7.5}
]

console.clear();

console.log('beerData:');
console.table(beerData);

var uniqueTypes = R.pipe(R.pluck('type'), R.uniq, R.sort((a, b) => a > b));
var typeList = uniqueTypes(beerData);

console.log('typeList: ', typeList);

var displayBeerByType = R.pipe(
    R.propEq('type'),
    R.filter(R.__, beerData),
    console.table.bind(console)
);


R.forEach(displayBeerByType, typeList);
Share