npm pitfalls

Lately we've been facing a number of issues with our npm workflow. I made a few notes how to prevent them in the future.

First, we use npm shrinkwrap to lock down production dependencies. And sometimes we find it broken for some reason. How it's happening.

Mocha + Webpack

There is one annoying issue with Webpack: if you use Webpack-specific code in your JS modules, it's impossible to use it outside of Webpack environment (or it takes time to made up a number of workarounds). A few days ago I was playing around with one awesome loader (sass-variable-loader) and it struck me again: I can share style variables with JSX views, but it makes them intestable, b/c mocha can't handle imports with Webpack loaders. So long story short: now I officially can state, that everything in my stack — production & development client builds, production & development builds for Node server and, finally, test builds — all compiled with Webpack.

Yo, ES6

The only thing I officially couldn't write using ES6 syntax / Babel is Yeoman generator. Even it's not something I write too often, this limitation is quite annoying when you actually write it. Here's the workaround I came up with.

Webpack Declarative

One of the greatest features in React is a declarative way to construct the things. Once you've tried it — you'll never go back to imperative mess. A few days ago, while rewriting some webpack configs, I finally figured out what's so annoying in the way we usually cook them — we cook them in imperative way.

Event handlers and context binding

Follow-up to my yesterday's post about dropdown blocks in React. There was a gotcha with event handlers and context binding. As you might already know, React.createClass has a built-in magic feature that bound all methods to this automatically. But this behaviour was removed from ES6 class syntax and you have to bind your methods to this explicitly.

So in yesterday's post I used this code to add / remove event listeners:

componentDidMount() {
  // Using ES7 functionBind syntax
  window.addEventListener('click', ::this._hideDropdownBlock, false);
  // Same in ES5
  window.addEventListener('click', this._hideDropdownBlock.bind(this), false);

componentWillUnmount() {
  window.removeEventListener('click', ::this._hideDropdownBlock, false);

This won't work: event listener will be added, but it won't be removed on component unmount. Here's why.