Today Icelab Learned
 about javascript

Detect Google Analytics blocking

In a world filled with ad blockers, you can’t rely on Google Analytics dependencies being available when you want to use them. We hit upon this problem when doing some outbound link tracking like so:

function trackOutboundLink (e, url) {
  e.preventDefault()
  ga('send', 'event', 'outbound', 'click', url, {
    'transport': 'beacon',
    'hitCallback': function() {
      document.location = url
    }
  })
}

If Google Analytics is blocked, this function would fail to log the outbound click and worse, it’d also stop the link from being followed at all. We can adjust to handle this more gracefully however:

function trackOutboundLink (e, url) {
  if (window.ga && window.ga.create) {
    e.preventDefault();
    ga('send', 'event', 'outbound', 'click', url, {
      'transport': 'beacon',
      'hitCallback': function() {
        document.location = url;
      }
    });
  }
}

By checking for both window.ga and window.ga.create before calling preventDefault we ensure that the links work when Google Analytics is blocked, and that outbound clicks are tracked whenever we can.

In React setState is not "guaranteed" to be synchronous.

I discovered recently that if you do something link this in react:

this.setState({reallyUseful: true})

if (this.state.reallyUseful) {
  this.doSomethingAmazing()
}

… it’s not very useful and more often than not nothing amazing happens, just confusion and sadness.

This is because setState it turns out, is asynchronous. Of course most of the time you are doing your amazing things in your render function so everything is sweet but on the odd occasion that you need to pass that value elsewhere, eg: publishing an event, beware!

From the docs:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

Read about setState here.

Editor’s note — setState will also take a callback as its second argument that’ll be executed after this.state has been updated:

this.setState(
  { reallyUseful: true },
  function() {
    if (this.state.reallyUseful) {
      this.doSomethingAmazing()
    }
  }
)

Using viewloader with turbolinks

When using viewloader with turbolinks, you want to make sure that viewloader executes once on the initial page load, once when the visitor reaches another page, and once when the visitor goes back in their browser (or forward for that matter).

To achieve this, get viewloader to execute on the ready, page:load and page:restore events:

$(document).on("ready page:load page:restore", function() {
  viewloader.execute(views);
});

page:restore is fired by turbolinks whenever “A cached body element has been loaded into the DOM”.

Using namespaced components in React

The last time I used React we had some pretty awkward workarounds for the fact that components couldn’t be namespaced. I was pretty stoked to find this has been fixed now.

So if you have a component myNavComponent you can do:

var NavComponent = React.createClass(...);

Define your sub-components as attributes:

NavComponent.Next = React.createClass(...);
NavComponent.Prev = React.createClass(...);
NavComponent.Goto = React.createClass(...);
var Nav = NavComponent;

And then use them as you would expect to in your JSX:

<Nav>
  <Nav.Prev />
  <Nav.Goto />
  <Nav.Next />
</Nav>

https://facebook.github.io/react/docs/jsx-in-depth.html#namespaced-components