How it feels to learn JavaScript in 2017

(For those who haven’t read it, this is a response to How it feels to learn JavaScript in 2016. Unlike other responses it includes complete code for an app like the one asked about.)


Hey, I got this new web project, but to be honest I haven’t coded much web in a few years and from what I’ve been reading it looks like the landscape changed a lot. You are the most up-to-date web dev around here, right?

-I guess you could say that.

Cool. I need to create a page that displays the latest activity from the users, so I just need to get the data from the REST endpoint and display it in some sort of filterable table, and update it if anything changes in the server. I was thinking maybe using jQuery to fetch and display the data? I know there are newer frameworks out there, but the more I read about them the more confused I get.

-Wasn’t jQuery the reason you quit web dev a few years ago?

Yeah, well I think I was doing it wrong. I couldn’t figure out why my app kept getting into weird states. Maybe you can teach me to organize my jQuery code better so I don’t get myself into so much trouble.

-It happens to the best of us. With jQuery you change the DOM in response to various events, and sometimes the sequence of events is different from what you expect. How you got to a particular state definitely can be confusing.

You aren’t convincing me I should get back into web dev.

-Wait; hear me out. With modern web frameworks you simply write how the state of your data maps to the state of your web page. It’s way less confusing.

OK let me think about that…wouldn’t that mean it redraws the page every time your data changes? I guess that could work. My users are all on desktop so it won’t be a big deal. It sounds like it would be really slow on mobile browsers, though.

-It doesn’t redraw the page every time. Modern frameworks are smart enough to figure out what part of the DOM needs to change and only touch that part.

Interesting. Which one should I use? The big ones are React, Angular and Ember, right?

-They’re all good. If you want to write your front-end logic in Handlebars use Ember. If you want to write your front-end logic with HTML attributes use Angular or Vue. If you want to write your front-end logic with JavaScript use React, Mithril or Inferno.

JavaScript, I guess. But doesn’t React use this other thing…JSX?

-JSX is just a JavaScript syntax extension. It lets you use HTML tags where you might otherwise write code that generates DOM elements.

What’s wrong with just using JavaScript?

-Nothing’s necessarily wrong. Actually Mithril’s documentation is all straight JavaScript. I’ve just found I get better feedback from people who do HTML/CSS all the time when I show them JSX code than I do when I show them straight JavaScript.

All straight JavaScript? I’m glad to hear I’m not the only one who isn’t entirely comfortable with JSX. You just made me want to try Mithril. Is it popular?

-It’s popular enough that it’s not going away, but nowhere near as popular as the bigger frameworks. I’ve actually been writing a pretty ambitious web app in Ember lately. But come to think of it, Ember hides certain things I’d like you to explicitly see when you’re getting up to speed, so I’d be happy to show you how you’d implement your app using Mithril.

Great! Can we set up a few hours soon when you can show me how to set up all the libraries, scaffolding and boilerplate code? Which is better these days, webpack or browserify? I have to confess, the build setup is the most intimidating part of modern web development for me.

-We can skip all that for now. Once you get a feel for the main part of modern web dev you can just copy a build setup I made with nothing more than babel and rollup. The build system is really only a small part of developing a modern web app.

Skip all that? But I want to see my web app actually work.

-You will; I’ll show you. Let’s code up your app using Mithril now. You said it was a filterable table, right? Let’s make planets.html a filterable table of planets.

<!DOCTYPE html>
<html>
<body>
<div id="app">Loading...</div>
<script src="https://unpkg.com/mithril/mithril.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/jsx" src="planets.jsx"></script>
</body>
</html>

OK, you told me what Mithril is. Another library? What’s Babel?

-Babel lets you to use modern JavaScript syntax that browsers don’t support it yet. You don’t absolutely need it, but it lets you forget what browsers do and don’t support and just code.

Oh wait, actually I think I read about that. Isn’t it bad to run a transpiler in your browser?

-Yes. It adds significant latency. But for learning, why not? It will be easy to change later. For now, let’s start writing your planets.jsx file by setting up the state of your app.

Just so you know, I have a meeting in 20 minutes. From what I’ve been reading, I know that when you say “state” things are about to get complicated. Maybe we should talk about Redux, Flux, etc. another time.

-We can talk about those when you’re writing an app that calls for them. With this app you just need two variables: your array of planets and a filtering function.

'use strict';
/** @jsx m */
let planets;
let planetFilter = planet => true;

Wait, shouldn’t that be var and not let?

-It’s the same, except let is block-scoped like variables in C or Java. No more hoisting.

Oh yeah, it’s been a while. I had forgotten about hoisting.

-You can go ahead and forget about it again. Just use let for variables you might reassign, and const for everything else.

You said that second one was a filtering function. Is that arrow just shorthand for the old-style function declaration?

-Yes, an arrow function is pretty much the same as the old function syntax with bind(this).

Oh yeah, I remember back when you helped me find that bug that we fixed by adding bind(this). I think I’m going to like these arrow functions.

-I bet you will. Now let’s make the top-level component for your app.

class PlanetApp {
view() {
return (
<section>
<PlanetFilters />
<PlanetTable planets={planets} />
</section>
);
}
}

That must be the new ES6 class syntax. I like the look of it. I’m not sure what to think of HTML mixed in with the JavaScript though.

-Don’t think of JSX as HTML mixed in with JavaScript. It’s equivalent to hyperscript, i.e. JavaScript for creating HTML elements. It’s important to understand that the JavaScript it compiles to doesn’t make a string; it makes an actual structure of elements. If, for example, your tags are unbalanced, it won’t compile.

OK, I’ll give it more time to see if I like it. Next can you show me the PlanetTable component?

-You bet. This is really the core of your app.

class PlanetTable {
view() {
return (
<table>
<tr>
<th>Name</th>
<th>Composition</th>
<th>Moons</th>
</tr>
{planets.filter(planetFilter).map(planet => <PlanetRow planet={planet} />)}
</table>
);
}
}

-Mostly it’s just static content, but on that one line you have a concise description of what your app does. It takes an array of planets, filters them to just the ones that should be shown, and that filtered array is mapped to HTML table rows.

Oh, I think I’m getting it now! That JSX syntax is just a JavaScript expression, so I can manipulate it however I want. I’m guessing the PlanetRow component is going to be really simple?

-Yes, possibly simpler than you think thanks to destructuring assignment.

class PlanetRow {
view(vnode) {
const { composition, name, moons } = vnode.attrs.planet;
return (
<tr>
<td>{name}</td>
<td>{composition}</td>
<td>{moons}</td>
</tr>
);
}
}

OK, so I guess vnode.attrs.planet is how you get that planet attribute that was passed in. There’s only one line with an equals sign so that must be…oh wow, destructuring assignment, where have you been all my life?

-I’m telling you, JavaScript is way more fun than it used to be. Here, let me show you how nice arrow functions are, even when you’re only considering their conciseness.

class PlanetFilters {
view(vnode) {
return (
<p>
<PlanetFilter
key="All"
func={planet => true} />
<PlanetFilter
key="Terrestrial"
func={planet => planet.composition === 'terrestrial'} />
<PlanetFilter
key="Gas Giant"
func={planet => planet.composition === 'gas giant'} />
<PlanetFilter
key="Ice Giant"
func={planet => planet.composition === 'ice giant'} />
</p>
);
}
}

OK, I see where you’re going with this. Those are filter functions. But I bet the event handlers you have to wrap around them can’t be that concise.

-They can with a little abstraction.

class PlanetFilter {
view(vnode) {
const { key, func } = vnode.attrs;
return (
<label>
<input type="radio" name="filter"
onchange={filterHandler(func)} /> {key}
</label>
);
}
}
function filterHandler(filterFunction) {
return function(event) {
if (event.target.checked) {
planetFilter = filterFunction;
}
};
}

-But you have a meeting to go to and you wanted to see this working. Here, let me toss some data into a separate planets.json file since you mentioned you needed to fetch data from the server. And now we just need code to fetch the data, put the data where your app can get it, and then mount the top-level component. Voila, it works.

m.request({url: 'planets.json'}).then(data => {
planets = data;
m.mount(document.getElementById('app'), PlanetApp);
});

Really, that’s it? Wow, last year this all seemed so intimidating! I have to run now, but I’m definitely looking forward to picking JavaScript up again. Thanks a bunch!

-Sure thing. I love talking about it. Any time.


Thanks to Biagio Azzarelli, Ben Chauvette, Garrick Cheung and Patrik Johnson for their feedback on drafts of this.

Show your support

Clapping shows how much you appreciated Bruce Lewis’s story.