parrisneeds.coffee code, music, drinks

JS - Vanilla JS in 2016 - Maybe the future is closer than we thought

This story starts with a recent job hunt. One of the companies I applied for asked me to create an interface that searches Twitch. Twitch, for those of you who don't know, is a site where people can stream their activities, mostly around gaming.

The prompt at a high level looked something like this:

  1. One week time limit
  2. Take your time, make it perfect, show us how you'd build a system
  3. No libraries
  4. Must use JSONP

GitHub: https://github.com/parris/twitchstreamsearch/

So, what do you do when people say "one week", "make it perfect", and "no libraries"... Well, if you're anything like me you'll basically make all your own libraries and spend an entire week doing it.

From the very get-go I started thinking about writing my own little mini-react and mini-redux. The next problem I knew I'd face was around module loading or cleanly splitting up my files. Step one for me though was actually to start-off with a jasmine like test runner so I can experiment and safely modify my code.

What followed from then on, was me learning that vanilla, plain-ol-js in 2016 is insanely powerful. Right now Chrome and FF support everything from

template literals

/src/components/Card.js#L23-L24

`${stream.game} - ${stream.viewers} viewers`

to arrow functions

/src/components/Header.js#L13-L18

(e) => {
    e.preventDefault();
    let queryInputEl = document.querySelector('.js-query-input');
    this.props.onSearch(queryInputEl.value);
}

to symbol property keys

/src/reducers/streams.js#L8-L12

const transformers = {
    [actionTypes.search]: function(state, action) {
        return [];
    },
    [actionTypes.searchComplete]: function(state, action) {

to classes

/src/components/Header.js#L9-L11

class Header extends Component {
    events() { ... }
    render() { ... }
}

to promises

/src/utils/request.js#L12

let requestPromise = new Promise(function(resolve, reject) { ... }

to block scoping, const/let, trailing commas, the fetch API, and more.

There were only 2 things missing from my typical Babel/Webpack world.

First off, I was missing a module loader and an easy way to split my files up. In this excercise, I relied on AMD to fill this gap. Functionally, AMD works similarly to the ES6 module proposal. It appears that ES6 modules will indeed be async in their final form. In addition, with the advancements around HTTP2 there should be no need for a separate build step, since HTTP2 prefers many smaller files over 1 large built file.

Modules in my excercise were defined like this:

define(function(require) {
    'use strict';

    const build = require('/src/utils/componentBuilder.js');
    const Component = require('/src/components/Component.js');
    const { Button, Div } = require('/src/components/BasicComponents.js');
    const i18n = require('/src/utils/i18n.js');

    return {};
});

Between "define" and "require" in the above example, the system figures out how to load the tree of dependencies in browser.

Second, Babel also helps us when we are writing React components. In this excercise, I created a component builder that mimics React's composibility. React of course could be used without sans JSX. While writing this post I tried to see if web components were in a mature enough state to use, but it seems that even Chrome doesn't really have support for them out of the box.

I've been working in Babel JavaScript land for quite sometime now (maybe the past 2 years). This is the first time it felt like I didn't need Babel/Webpack anymore. We still seem to be missing a few key elements (modules and components), but we don't seem that far off, and as my example proved those limitations can be overcome today. Maybe the future is closer than we thought! Maybe someday we can drop these bulky build steps!

Links:

  1. GitHub: https://github.com/parris/twitchstreamsearch/
  2. Live Demo
  3. Test Runner