Implementing a Mini-React-Redux Framework on a Django Page

Introduction

I have built several production web applications using React and Redux and generally have had an excellent experience with those technologies.  One of React’s greatest assets IMO is it’s ability to integrate into all kinds of stacks and setups but still play nice with the other kids.  That was something that impressed me back in Spring 2014 when I first used React.  We got React running in the jQuery spaghetti code of a massive, legacy Ruby on Rails application with incredibly little effort and huge productivity benefits to the team.  Redux is also incredible for the amount of good it does you with so little code.

There are lot’s of blogs and tutorials on how to build a full single-page application (SPA) complete with client-side routing, persistent state, and even server-side rendering to boost that time-to-interactivity metric.  What if I don’t need that?  What if I already have a site built using an “old-school” server-side framework like Ruby on Rails or Django, but I have one specific page that should be highly interactive and need something more robust than simple jQuery?  React and Redux could still be hugely beneficial, but how do I do it without (a) getting bogged down in boilerplate or (b) over-engineering the solution?

Mini React-Redux Framework to the rescue!

Ready, Set, Go!

Let’s make the skeleton of a super, tiny JavaScript framework that can fit our use case for a Django website.

Here are the steps we’ll follow:

  1. Install our client dependencies
  2. Setup Webpack with Django
  3. Implement the Mini React-Redux Framework

Install our client dependencies

The following are the NPM dependencies I am relying on:

{
  "dependencies": {
    "babel-core": "~6.3.26",
    "babel-loader": "~6.2.0",
    "babel-preset-es2015": "~6.3.13",
    "babel-preset-react": "~6.16.0",
    "react": "~15.4.2",
    "react-dom": "~15.4.2",
    "redux": "~3.6.0",
    "redux-logger": "~2.7.4",
    "redux-thunk": "~2.2.0",
    "webpack": "~1.13.2",
    "webpack-bundle-tracker": "0.0.93"
  }
}

Include these dependencies in your package.json and run npm install.

Setup Webpack with Django

For this step, we are going to use the django-webpack-loader tool to give us the power to load Webpack bundles onto a templated page.  The setup is very simple if you have a vanilla Django application; just follow the loader tutorial.  If you are using Django-Mako-Plus add-on, supplement the regular loader tutorial with my own little tutorial.  I will give a high-level overview.

We need a webpack.config.js. Here is a pre-v2 webpack config file that we can use:

var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');

module.exports = {
  context: __dirname,
  entry: {
    myapp: './client/myapp/index',
  },
  output: {
      // IMPORTANT: Need to match this up with settings STATICFILES_DIRS and WEBPACK_LOADER
      path: path.resolve('./static/bundles/'),
      filename: "[name]-[hash].js",
      // OPTIONAL: In this setup, it can be helpful to namespace the exported files
      library: 'MyCompanyApp',
      libraryTarget: 'var'
  },

  plugins: [
    // IMPORTANT: django-webpack-loader needs to know where this file is
    new BundleTracker({filename: './webpack-stats.json'})
  ],

  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          plugins: ['transform-runtime'],
          presets: ['es2015', 'react', 'stage-2'],
          cacheDirectory: true
        }
      }
    ]
  }
}

You will need to throw in some settings for the Django webpack loader plugin so it knows where to find certain key files. Here are some simple defaults:

INSTALLED_APPS = (
    # ...
    'webpack_loader'
)

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

WEBPACK_LOADER = {
    'DEFAULT': {
        # Lets not cache for dev builds; you can enable for prod builds
        'CACHE': False,
        # NOTE: where, inside the staticfiles, are the output? Must end with slash
        'BUNDLE_DIR_NAME': 'bundles/',
        # NOTE: STATS_FILE is the path to the file that the BundleTracker webpack plugin is writing.
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
        'POLL_INTERVAL': 0.1,
        'TIMEOUT': None,
        'IGNORE': ['.+\.hot-update.js', '.+\.map']
    }
}

That should do for now.

Implement the Mini React-Redux Framework

Dan Abramov is a smart guy. He wrote Redux. He encourages devs not to use Redux until you know that you need it; just use Props and State. I strongly support that! However, in this post I want to demonstrate the more complicated case of using Redux including with some middlewares just to show how simple it is. I encourage you to pair this example down to only what you need.

Here is the source I came up with for our mini framework:

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import createLogger from 'redux-logger'
import MyComponent from './components/MyComponent'

/**
 * Redux Reducer.
 * @params:
 *  - state: the previous state of the store
 *  - action: an object describing how the state should change
 * @returns:
 *  - state: a new state after apply appropriate changes
 */
const rootReducer = (state = { clicks: 0 }, action) => {
  // ... change state based on action
  return state
}

/**
 * Redux Store object with three functions you should care about:
 *  - getState(): returns the current state of the store
 *  - dispatch(action): calls the reducer with a given action
 *  - subscribe(): called after a reducer runs
 *
 * The store has two optional middlewares to showcase how you would add them:
 *  - redux-thunk: allows `store.dispatch()` to receive a thunk (function) or an object
 *                 See http://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout/35415559#35415559
 *  - redux-logger: logs out redux store changes to the console. Only in dev.
 */
const middlewares = process.env.NODE_ENV === 'production'
    ? applyMiddleware(thunk)
    : applyMiddleware(thunk, createLogger())
let store = compose(middlewares)(createStore)(rootReducer)

/**
 * Helper function to render the Gradebook component to the DOM.
 * Makes the following props available to the Gradebook component:
 *  - storeState: an object of the latest state of the redux store.
 *  - dispatch: a function that dispatches actions to the store/reducer.
 */
const render = (nodeId, component) => {
  let node = document.getElementById(nodeId)
  ReactDOM.render(<component storeState={store.getState()} dispatch={store.dispatch} />, node)
}

/**
 * Function that bootstraps the app.
 *  - render the component with initial store state.
 *  - re-render the component when the store changes.
 */
const start = () => {
  render('app', MyComponent)
  store.subscribe(() => render('app', MyComponent))
}

To start the application, (a) load the bundle on the page and (b) call the exported start function when the page loads. Here’s an example using jQuery:

{% load render_bundle from webpack_loader %}
{% render_bundle 'myapp' %}

<script>$(() => MyCompanyApp.start())</script>

Explanation

This little proof-of-concept is interesting to me because of how much usefulness it provides with so little code.  With this code, we create a Redux store with some basic middlewares and a reduce that does nothing interesting (yet).  Then we render a component to the DOM, giving it the current store state and a function for the component to dispatch actions if necessary, and setting up a store subscription so that the component will be re-rendered whenever the store changes.

Another cool part about this approach is that a lot of the setup code can be pulled out and made reusable.  The render(), start(), and store setup would probably be the same for every Mini App we would create.  Then we could simplify this whole file down to just the reducer and passing in the node and component to the start function (not implemented here).

Conclusion

With very little effort and Boilerplate, we have a React application using Redux as it’s storage system.  With this in place, you can build quite sophisticated widgets and still have the flexibility to get more complex if you need to do something more involved.

Implementing a Mini-React-Redux Framework on a Django Page

Defending JavaScript

In a forum thread, a member brought up the following critiques of JavaScript.  I quickly recognized many of these arguments as ones I have heard before and really wanted to address. In my attempt to not hijack the thread (which was not about “To JavaScript, or Not To JavaScript”), I collected my thoughts here.  This is not meant to be a passive-aggressive post, but rather as an aboveboard rebuttal in a logical discussion.

The Argument Against JS:

…(skipped for brevity)…

I think Javascript has gone a long ways since [I] first started using it, but here are some issues that I have had with it over the time I have used it:

  1. Javascript has a lot of “cute” and “neat” tricks in it, and I feel like people abuse those tricks constantly. In Python there are some cute tricks that you can use, but the community tends to frown upon it.
  2. NPM is such a good and bad experience. Compared to some other package systems its kind of messy. The other issue is that they have this
    “microservice” where instead of writing a one line piece of code they instead pull from NPM to get the same thing done. There was an issue a few months ago where one developer removed his package that thousands of people depended on, and it caused a “dependency hell” per se.
  3. Documentation is lousy on almost all javascript projects. The documentation tools for javascript projects are pretty lousy. When you have worked with docutils/sphinx for python you start to wonder what is wrong with the javascript documentation process.
  4. Lack of stability – this is getting better, but still pretty lousy at times. Almost all javascript projects including node has this issue. Everybody is so into “progressing” the platform that they push
  5. Too many kludges to make it imitate OO
  6. Poor unittesting tools.

My Defense of JavaScript

The arguments above use Python as a language reference point. That’s great! I have used Python for years and love it as well; so I will focus on comparing JS with Python.

1. (Terrible) Language Tricks

Every language has “cute and neat tricks” (read terribleness). JavaScript has more than some languages, but also less than other languages. Many of the “terrible things” in JS are a result of how it runs in the browser (DOM, globals, namespacing) rather than language problems (although JS itself has some bad ones)

The BIG difference with JS you are forgetting is that almost every language can break compatibility relatively freely (e.g. Python 3, Ruby 2, Lua [every version]). JavaScript can’t, because it would break the internet. Bad, wrong-headed decisions can not be removed once people start using them. Websites need to stop using features before they can be removed. Very few other languages have these strict deprecation requirements.

Python has it’s share of weirdness that people use and had a rather large set of breaking changes with Python 3.  Let’s read from the official sources about Python 3:

There are more changes than in a typical release, and more that are important for all Python users. Nevertheless, after digesting the changes, you’ll find that Python really hasn’t changed all that much – by and large, we’re mostly fixing well-known annoyances and warts, and removing a lot of old cruft.

Python had (still has) a big problem with Python 2.7 -> Python 3 upgrades.  Even now, years later, many projects still rely on 2.7 and haven’t upgraded. That situation would never work on the world wide web!

2. NPM

Your argument is against how people have used NPM, not against NPM itself. And the issues you cited are definitely problems, but they are problems for every package manager that reaches the nexus of popularity and ease-of-use. RubyGems had the exact same problem with excessive “micro-gems” 10 years ago.

And that NPM “left-pad” issue that broke everything earlier this year … yeah that could still happen on NPM, PyPi, RubyGems, INSERT_PACKAGE_MANAGER.  Nothing special/bad about NPM made it happen.  Just that a guy decided to be a jerk and remove a package that everyone depended on.

Compare PyPi to RubyGems and NPM and it makes sense why it was such a big deal for NPM: PyPi is notoriously fractured and wierd to publish on (hence smaller); RubyGems and NPM are notoriously easy (hence bigger). The problem of package management is a classic hard problem that nobody has figured out completely from JavaScript, Python, and Ruby to widely used Linux distributions.

3. Docs

JavaScript sucks because a lot of projects don’t write good docs? Not sure I follow the argument. But if you wanted to argue it, you could easily attribute that to the massive number of JavaScript projects vs. Python projects. JavaScript actually has many excellent automated tools for documention.

4. Instability

Patently false. Microsoft, Google, Facebook, Walmart, Mozilla, etc. have poured so much time, effort, and money into the JS ecosystem (specifically Node.js, NPM, and JS Engine implementations) that it has become one of the most stable platforms you can be on. And don’t forget the JS language guarantee that language features can’t be removed until most websites stop using them. Even among browsers, the consistency of good implementation of JS features is at an all time high.

Any instability in Node.js specifically has been largely mitigated with the new release process (Stable and Current distributions). The only “instability” to speak of is the massive volume of updates that V8 goes through to keep up with ECMAScript features, and those only matter to the native library maintainers not using node-gyp (which most use afaik). And even then, Google and Microsoft now work closely with Node.js maintainers to help with API changes in their JS engines.

5. Not OOP

Everything in JS is an object. It just doesn’t use Classical OO. Your argument sounds more like you mean Classical OO vs. Prototypal OO. JS is the latter, and it is just as Object-oriented as Classical; but fundamentally different because Prototypes are objects as well and can be changed at runtime.  To help people wrap their heads around prototypes, ES6 even introduced the keyword class (although I’m not a big fan of it).  In some ways, prototypes are a more true and powerful kind of OOP.  Classes were introduced after OOP landed and primarily to help with static-type checking, not to enable better OOP design.

In the end, JavaScript is multi-paradigm just like Python with a mix of Object-oriented, Functional, and Imperative paradigms.

6. No Unit Testing

Patently false. You will undoubtedly find many testing libraries of very high quality in JS.  And if you want to talk culture of testing in a community; in my experience, in a room with a Rubyist, Pythonista, and JavaScripter, the Python guy is the least likely to be writing tests.

Conclusion

JavaScript as a language, with all the warts and weirdness, is easily one of the fastest evolving languages in the world.

10 years ago, who would have thought this about JS:

  1. Most widely used programming language on earth
  2. One of the fastest scripting languages ever
  3. Largest package ecosystem ever (npm)
  4. Popular as a server backend language

I once shared your disdain of JavaScript, but with the recent incredible work being done on the language itself, it has become one of my favorites.

I’ll close with this slide by Brendan Eich, the creator of JavaScript:

Screenshot 2016-09-01 17.53.16

Defending JavaScript

Optimizing the Performance of a Node.js Function

Introduction

After letting it stagnate for awhile, I decided to rework Street.js to use the things I have been working with in Node.js this last year.  My main goals are as follows:

  • ES6ify the code base
  • Replace nasty callback code with Promises
  • Pass ESLint using JavaScript Standard Style config
  • Annotate types with Flow
  • Simplify the implementation

Node.js v4 has a lot of new ES6 features that are extremely helpful, fun, and performant.  I will be refactoring Street to use these new features.  Of course, like a good semver citizen, I will update my major version (to v1.0) when I publish the rewrite.

Setup

I am using Babel as a transpiler to paper over ES6 features not yet in Node.js, but blacklisting the transforms that are already present.  Many ES6 features (e.g. generators, symbols, maps, sets, arrow functions) are more performant natively than via transpilation and I do not care about supporting Node.js before version 4.  The following is my .babelrc configuration file showing the blacklist I am using:

{
  "blacklist": [
    "es3.memberExpressionLiterals",
    "es3.propertyLiterals",
    "es5.properties.mutators",
    "es6.blockScoping",
    "es6.classes",
    "es6.constants",
    "es6.arrowFunctions",
    "es6.spec.symbols",
    "es6.templateLiterals",
    "es6.literals",
    "regenerator"
  ],
  "optional": [
    "asyncToGenerator"
  ]
}

Case Study: Walking a Directory

In Street, I need to walk a directory of files so I can generate a manifest of file paths and their hashes for comparison to a previous manifest.  The directory walking code was hairy; most of it was from Stack Overflow.  Here’s the current state (cleaned up):

var fs = require('fs')
var path = require('path')

function oldFindFilePaths (dir, done) {
  var filePaths = []
  fs.readdir(dir, function(err, filelist) {
    if (err) return done(err)
    var i = 0

    ;(function next() {
      var file = filelist[i++]
      if (!file) return done(null, filePaths)

      file = path.join(dir, file)

      fs.stat(file, function(err, stat) {
        if (err) return done(err)
        if (stat && stat.isDirectory()) {
          _findFilePaths(file, function(err, res) {
            if (err) return done(err)
            filePaths = filePaths.concat(res)
            next()
          })
        } else {
          filePaths.push(file)
          next()
        }
      })
    })()
  })
}

I never really liked this because it is not intuitive to me. The function is an unwieldy set of multiple recursive calls that make me feel gross.  Once I got it working, I was wary of touching it again.

There must be a better way to do this! I can either spend some time refactoring this to make it nicer, or see if a rewrite is more elegant and perhaps performant. I am willing to take a small performance hit.

The following is my first iteration:

import fs from 'fs'
import path from 'path'

async function findFilePaths (dir: string): Promise<Array> {
  var foundPaths = []
  var files = fs.readdirSync(dir)

  while (files.length > 0) {
    let file = files.pop()
    if (!file) break

    let filePath = path.join(dir, file)
    let stat = fs.statSync(filePath)

    if (stat.isDirectory()) {
      foundPaths = foundPaths.concat(await findFilePaths(filePath))
    } else {
      foundPaths.push(filePath)
    }
  }

  return foundPaths
}

Do not be thrown off by the Type Annotations.  I really enjoy FlowType and find it useful for finding many kinds of bugs.  All those annotations get stripped during babel transpilation.

This function was much clearer. I love ES7 Async functions. They wrap a function’s logic in a Promise and then resolve with the returned value or reject if errors are thrown. Inside an Async Function, you can await on Promises. If they resolve, the value resolved with is returned. If they reject, the value (best if an error instance) rejected with is thrown.

Notice that I replaced my asynchronous fs calls with synchronous ones. The callbacks were just too nasty, and since this is a CLI application they were not that helpful for performance as I was using them.

This was much clearer, but still not ideal to me. I am not a fan of while loops and instead prefer a more functional approach using map, filter, and reduce when possible. Also, calling fs.readdirSync was ok in this usage, but those fs.statSync calls seemed inefficient as they would block on each call to a file descriptor. Perhaps I could make them async again but parallelize them.

This lead me to my next iteration:

async function newFindFilePaths2 (dir: string): Promise<Array<string>> {
  var files = await new Promise((resolve, reject) =>; {
    fs.readdir(dir, (err, files) =>; err ? reject(err) : resolve(files))
  })

  var statResultPromises = files.map(file =>; new Promise((resolve, reject) => {
    var filepath = path.join(dir, file)
    fs.stat(filepath, (err, stat) =>; err ? reject(err) : resolve({filepath, stat}))
  }))

  var results = await Promise.all(statResultPromises)
  var {subDirs, foundPaths} = results.reduce((memo, result) =>; {
    if (result.stat.isDirectory()) {
      memo.subDirs.push(result.filepath)
    } else {
      memo.foundPaths.push(result.filepath)
    }
    return memo
  }, {subDirs: [], foundPaths: []})

  var subDirPaths = await Promise.all(subDirs.map(findFilePaths2))
  return foundPaths.concat(...subDirPaths)
}

Notice the while loop is gone; replaced with map and reducefs.stat happen in parallel for a list of files. The fs.readdir call is also async because I will do recursive calls to this function in parallel for all subdirectories I find.

I am also a fan of destructuring and spreading. It makes for more concise and elegant code. My favorite example here is taking the results of recursive calls to findFilePaths2, which are arrays of strings, and then spreading them into arguments to the foundPaths.concat function call to join all the paths into a single array.

This is excellent, but can be cleaned up and broken into a few different functions. This brings me to my last iteration:

function listFiles (dir) {
  return new Promise((resolve, reject) => {
    fs.readdir(dir,
               (err, files) => err ? reject(err) : resolve(files))
  })
}

function getStatMapFn (dir) {
  return file => new Promise((resolve, reject) => {
    var filepath = path.join(dir, file)
    fs.stat(filepath,
            (err, stat) => err ? reject(err) : resolve({filepath, stat}))
  })
}

function partitionByType (memo, result) {
  if (result.stat.isDirectory()) {
    memo.subDirs.push(result.filepath)
  } else {
    memo.foundPaths.push(result.filepath)
  }
  return memo
}

async function newFindFilePaths3 (dir: string): Promise<Array<string>> {
  var files = await listFiles(dir)
  var results = await Promise.all(files.map(getStatMapFn(dir)))
  var {subDirs, foundPaths} = results.reduce(partitionByType,
                                             {subDirs: [], foundPaths: []})

  var subDirPaths = await Promise.all(subDirs.map(findFilePaths3))
  return foundPaths.concat(...subDirPaths)
}

Even though it is more lines of code, I prefer this to the previous. A few pure, helper functions and one function that puts them all together concisely and elegantly. So beautiful!

Running Times Compared

Lets check the execution time to see if we did any better.  This is just meant as a dirty comparison, not super scientific.

Function Execution Time (11 files, 2 dirs)
oldFindFilePaths  (callback hell) 1.8 ms
newFindFilePaths  (while loop) 12.1 ms
newFindFilePaths2  (map/reduce) 13.3 ms
newFindFilePaths3 (final map/reduce) 13.4 ms

Darn! The old function appears to be the most performant with a small number of files.  The difference between my last two iterations is negligible which makes sense because they are really the same thing just refactored slightly.

But what happens when there are more files and subdirectories?

Function Execution Time (11 files, 2 dirs) Execution Time (1300 files, 200 dirs) Execution Time (10800 files, 2400 dirs)
oldFindFilePaths  (callback hell) 1.8 ms 41.8 ms 269.6 ms
newFindFilePaths  (while loop) 12.1 ms 36.9 ms 182.6 ms
newFindFilePaths2  (map/reduce) 13.3 ms 60.8 ms 413.8 ms
newFindFilePaths3 (final map/reduce) 13.4 ms 61.5 ms 416.1 ms

Interesting!  The synchronous while loop started beating all the cases once files started to be in the 1000s spread over 100s of subdirectories.

Conclusion

I think I will probably end up going with the while loop function because it is the simplest and has better performance at scale.  And in the end, I mainly just wanted something with a simple API that I could hide behind a promise.

My theory of its superior performance over large directories is that the synchronous file system queries act like a kind of naive throttling system; it stops the VM from making thousands of concurrent function calls and file system queries which would bog it down.  That’s just my intuition though.

Optimizing the Performance of a Node.js Function

Header Files, Compilers, and Static Type Checks

Have you ever thought to yourself, “why does C++ have header files”?  I had never thought about it much until recently and decided to do some research into why some languages (C, C++, Objective C etc.) use header files but other languages do not (e.g. C# and Java).

Header files, in case you do not have much experience with them, are where you put declarations and definitions.  You declare constants, function signatures, type definitions (like structs) etc.  In C, all these declarations go into a .h file and then you put the implementation of your functions in .c files.

Here’s an example of a header file called mainproj.h:

#ifndef MAINPROJ_H__
#define MAINPROJ_H__

extern const char const *one_hit_wonder;

void MyFN( int left, int back, int right );

Here is a corresponding source file mainproj.c:

#include "mainproj.h"

const char const *one_hit_wonder = "Yazz";

void MyFN( int left, int back, int right )
{
    printf( "The only way is up, baby\n" );
}

Notice that the header only has the function definition for MyFN and it also does not specify what one_hit_wonder is set to. But why do we do this in C but not in Java?  Both are compiled and statically typed.  Ask GOOGLE!

A great MSDN blog post by Eric Lippert called “How Many Passes” was very helpful.  The main idea I got out of the article is that header files are necessary because of Static Typing.  To enforce type checks, the compiler needs to know things like function signatures to guarantee functions never get called with the wrong argument types.

Eric lists two reasons for header files:

  1. Compilers can be designed to do a single pass over the source code instead of multiple passes.
  2. Programmers can compile a single source file instead of all the files.

Single Pass Compilation

In a language like C#, which is statically typed but has no header files, the compiler needs to run over all the source code once to collect declarations and function signatures and then a second time to actually compile the function bodies (where all the real work of a program happens) using the declarations it knows about to do type checks.

It makes sense to me that C and C++ would have header files because they are quite old languages and the CPU and Memory resources required to do multiple passes in this way would be very expensive on computers of that era.  Nowadays, computers have more resources and the process is less of a problem.

Single file compilation

One interesting other benefit of header files though is that a programmer can compile a single file.  Java and C# can not do that: compilation occurs at the project level, not the file level.  So if a single file is changed, all files must be re-compiled.  That makes sense because the compiler needs to check every file in order to get the declarations.  In languages with header files, you can only compile the file that changed because you have header files to guarantee type checks between files.

Relevance Today

Interesting as this may be, is it relevant today if you only do Java, C#, or a dynamic language?  Actually it does!

For instance, consider TypeScript and Flow which both bring gradual typing to JavaScript. Both systems have a concept of Declaration files.  What do they do?  You guessed it!  Type declarations, function signatures, etc.

TypeScript Declaration file:

module Zoo {
  function fooFn(bar: string): void;
}

Flow Declaration file:

declare module Zoo {
  declare function fooFn(bar: string): void;
}

To me, these look an awful lot like header files!

As we see, header files are not dead!  They are alive and well in many strategies for Type Checking.

Header Files, Compilers, and Static Type Checks

Why I did not like AngularJS in 2014

Edited Mar, 2015:  Previously titled “Why I Do Not Recommend Learning AngularJS”.  In retrospect, my arguments are superficial and likely apply to the specific situation I was in.  In addition, I was wrong that learning a new tech is wasteful.  Learning anything makes you better at learning and that is what we should all be trying to do.  Learn what you’re excited about!

tl;dr

Despite it’s good qualities, I did not enjoy learning AngularJS.  With all the available options of web frameworks (e.g. Ember, React, Backbone, etc.), Angular fell behind in the following three areas:

  1. Performance
  2. Complexity
  3. Non-transference of Skills

Introduction

A lot of people ask me what I think about AngularJS, so I wanted to take some time to collect my thoughts and try to explain it clearly and rationally.  The following is the result.

I would like to start by saying AngularJS has a lot of good qualities, or else not so many people would use it so happily.  It makes developers excited to do web development again and that is hugely important.

With that being said, I did not like learning AngularJS.  With all the available options of web frameworks (e.g. React, Ember, Backbone, etc.), Angular falls behind in the following three areas:

  1. Performance
  2. Complexity
  3. Non-transference of Skills

Performance

I normally do not like picking on performance flaws, especially when a conscious decision has been made to trade performance for productivity.  I can understand that trade-off.  I do Ruby on Rails 😉

However, Angular’s performance has such serious problems that it becomes almost unusable for certain features or whole applications.  The threshold of how much work you can make Angular do on a page before performance tanks is scary low!  Once you have a couple thousand watchers/bindings/directives doing work on a page, you notice the performance problems.  And it is not actually that hard to get that large amount of binding happening on the page.  Just have a long list or table with several components per row, each with healthy number of directives and scope bindings, and then add more elements as you scroll.  Sound like a familiar feature?

Again I’d like to say that performance is not that terrible of a problem to have, because new versions of a framework can (and almost always will) optimize around common, performance problems.  I do not think performance will be a long-term problem in Angular; but it is a problem right now.

Complexity

Of all the most popular front-end frameworks (Ember, React, and Backbone), Angular is the most complex.  Angular has the most new terms and concepts to learn in a JavaScript framework such as scopes, directives, providers, and dependency injection.  Each of these concepts are vital to effectively use Angular for any use case beyond trivial.

Ember is also quite complex, but the framework itself gives direction for project organization which mitigates some complexity.  Also Ember is better at mapping its concepts to commonly used paradigms which I will talk about in the next section.

With React, you can be productive after learning a few function calls (e.g. createClass() and renderComponent()), creating components with objects that implement a render() method, and setting your component state to trigger re-renders.  Once you wrap your head around what React is doing, it is all very simple.  My experience was after weeks with Ember and Angular, I still did not grok all the complexity or feel like a useful contributor to the project.  After a day with React, I was writing production quality UI with ease.

Non-transference of Skills

I have been a web developer for years now.  Not a lot of years, but a few.  My first dev job was in college building UI with jQuery, which I learned very well.  Then I remember my first job interview outside of school with a company that built web applications with vanilla JavaScript and no jQuery.  I got destroyed in the JavaScript portion of the interview because my jQuery knowledge mapped very poorly to vanilla JavaScript.  In fact, I would go so far to say that I knew next to nothing about JavaScript even after a year of extensive web development with jQuery.

Why didn’t my jQuery skills transfer?  Because my development with jQuery taught me a Domain Specific Language (DSL).  While DSL’s can improve productivity, knowledge of them will seldom transfer to other areas.  The reverse can also be true.  You could call this inbound and outbound transference.

Angular is like jQuery.  It has transference problems.  The most serious problem in my mind is that Angular suffers from both inbound and outbound transference problems.  Knowing JavaScript, MVC, or other frameworks was less helpful while learning Angular.  What I learned from doing Angular has not helped me learn other things.  But maybe that’s just me.

Conclusion

If you know Angular and are productive with it, great!  Use it.  Enjoy it.  Be productive with it.  I tried Angular, and it just didn’t do it for me.

If you are looking for a framework that is both scalable and flexible, look into React.  In my experience, it is the easiest to learn and plays the nicest with legacy code.  Iterating React into almost any project is quite easy.  Of all the frameworks, React is probably the easiest to get out of because all your application logic is in pure JavaScript instead of a DSL.  The strongest benefit I have seen when using React is the ability to reason about your app’s state and data flow.  If you want a high-performance and transferable application, I highly recommend React.

If you want the experience of a framework that does a lot for you, go for Ember.  It will arguably do more for you than even Angular.  As I have seen, the Ember team is also more responsible/devoted to supporting large-scale applications or corporate clients which require stability and longevity.  They are the clients who do not want to be rewriting their apps every other year.  The one drawback I have seen is that Ember prefers to control everything of your app and does not play nice with other technologies.  If you have substantial legacy code, Ember will be a problem.

AngularJS will be releasing 2.0 soon, and it will be completely different from Angular 1.x.  Controllers, Scopes, and Modules are all going away.   To me, that seems like realization by the Angular Core Team that some of those neo-logisms did not work out.

Why I did not like AngularJS in 2014

Lazy-loading Embedded Videos

At MyEducator we do a lot of work with embedded, video content from our course creators.  As of the date of this article we use Vimeo Pro accounts which have worked well thus far, but we will probably migrate to something more commercial in the future.  Vimeo is not naturally supportive of business 🙂

A problem we ran into on a few courses was that we had too many videos embedded on the same page.  While the page loaded relatively quickly, when you tried to play a video it would delay for several minutes as all of the videos tried to load into the page.  That is just an unacceptable amount of wait time, so I came up with a straightforward way to lazy load the videos when they appear on screen.

Continue reading “Lazy-loading Embedded Videos”

Lazy-loading Embedded Videos