Give Your Product a Leg Up

The Clever Beagle blog is a great place to sharpen up your skills, keep your inspiration high, and stay motivated when building your product. All it takes is popping in an email below to get our latest and greatest in your inbox.

Pupv1.6.1

June 27th, 2018

  • fixed #176

    Fix flash of /login page while SSR is loading.

  • improved #166

    Add minimum character length validation to update password on <Profile />.

  • improved #165

    Upgrade to Meteor 1.7.0.1+.

  • fixed #161

    Fix PropTypes warning on <BlankState /> component.

  • improved #162

    Remove fourseven:scss dependency.

  • added #156

    Add <Authorized /> route component for roles-based routing.

  • added #155

    Add settings panel (relates to #149).

  • added #151

    Add a simple admin panel for users.

  • added #149

    Add a pattern for acquiring GDPR consent.

Ryan Glover
Tutorial: How to Load Third-Party Scripts Dynamically in JavaScript

Tutorial: How to Load Third-Party Scripts Dynamically in JavaScript

When we're building single-page or JavaScript driven applications using tools like Pup, from time to time we need to rely on third-party libraries that require a script tag being placed in the <head></head> or <body></body> of our HTML.

For libraries that are used globally across the site, placing these in our main <head></head> is perfectly fine.

For libraries that are only used in one or a few places, placing calls to load these libraries in our application's <head></head> can add unnecessary page load, and subsequently, load time.

Fortunately, there's a workaround.

The trick is to create the <script></script> tag loading the library on the fly and inject it into the DOM only when we need it. Here's the gist of it:

Dynamic Script Loading Template

const loadDynamicScript = (callback) => {
  const existingScript = document.getElementById('scriptId');

  if (!existingScript) {
    const script = document.createElement('script');
    script.src = 'url'; // URL for the third-party library being loaded.
    script.id = 'libraryName'; // e.g., googleMaps or stripe
    document.body.appendChild(script);

    script.onload = () => {
      if (callback) callback();
    };
  }
};

The idea here is that we create a function that we can call on the pages where we need the third-party library and dynamically create and inject the <script></script> tag into the <body></body> of the application.

To make this concrete, let's assume we're trying to load the Google Maps API to display a map on our page:

Loading Google Maps Dynamically

const loadGoogleMaps = (callback) => {
  const existingScript = document.getElementById('googleMaps');

  if (!existingScript) {
    const script = document.createElement('script');
    script.src = 'https://maps.googleapis.com/maps/api/js?key=<API Key>&libraries=places';
    script.id = 'googleMaps';
    document.body.appendChild(script);

    script.onload = () => {
      if (callback) callback();
    };
  }
};

Let's step through it. Inside of our function, we begin by trying to detect whether or not we've already loaded Google Maps by looking for a <script></script> tag with its id set to googleMaps. If we do, we stop—there's no need to load the library twice.

If we don't find an existingScript, however, we go to actually create the script dynamically. We start by creating an empty <script></script> tag in memory as script and then assign the necessary attributes to it src (the URL where the script lives) and the id to identify the script later. Finally, we append the script to our <body></body> tag to actually load it.

One last detail: notice the call to script.onload here? This is to give us control over loading elements in our UI that are dependent on the script existing. The callback argument passed to our loadGoogleMaps function here is designed to call some code after the library has loaded.

Putting this into practice, here's an example of putting this to use in a React component that exists in Pup:

Example Usage

import React from 'react';
import GoogleMap from '../GoogleMap/GoogleMap';
import loadGoogleMaps from '../../../modules/load-google-maps.js';

const MapsComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { googleMapsReady: false };
  }

  componentWillMount() {
        loadGoogleMaps(() => {
          // Work to do after the library loads.
          this.setState({ googleMapsReady: true });
        });   
  }

  render() {
      return (
          <div className="MapsComponent">
                {this.state.googleMapsReady ? <GoogleMap /> : ''}
      </div>
    );
  }
}

That's it! Now, when our component is beginning to mount, we'll call to our library to dynamically create and inject our <script></script> into the <body></body>. Once it's loaded and calls to the script.onload function, the callback we pass here will fire and update our component's this.state.googleMapsReady value to be true. If we look down in our render function, we can see a contrived example of this at play. Here, we only load our <GoogleMap /> component if this.state.googleMapsReady is true, meaning, our library is loaded and ready for use.

Neat, right? Though we've focused on Google Maps as our example here, this will work for any third-party library that needs to be loaded via a remote URL.

Pupv1.5.1

May 03rd, 2018

  • fixed #159

    Fix incorrect CSS variable referencing old Sass syntax. H/t @mbessieres for the tip.

Pupv1.5.0

April 26th, 2018

  • improved #157

    Bump Meteor to 1.6.1.1 and all dependencies to their latest compatible versions.

  • added #154

    Add blank state component.

  • improved #144

    Migrate to base template for transactional emails. H/t @michelfloyd for this one.

  • improved #140

    Move "Application Name" and support email into settings file instead of hard-coding it into the source. H/t @michelfloyd for this one.

  • improved #138

    Correct all linter errors. H/t @merlinpatt for this one.

  • improved #134

    Prevent welcome email sending before a user has verified their email address. h/t @toinevk.

  • improved #130

    Remove unnecessary Babel presets. h/t @toinevk.

  • improved #81

    Migrate to styled-components for CSS.

  • added #69

    Support for SSR with examples for dynamic data.

Ryan Glover
Look For Simple Fixes First

Look For Simple Fixes First

Over the past few weeks, I've been focused on building out the infrastructure for a new product we'll be offering at Clever Beagle soon, Pupgrades.

Because Pupgrades will be available on a pay-per-item basis, I decided that it'd be worthwhile to integrate the marketing and checkout flow into the existing Clever Beagle app (the one we use with our mentorship customers to track work on their products).

In theory this was a great idea, but in practice there was a little hiccup: the app wasn't built with SEO in mind; all of the HTML for the app was rendered on the client only. While Google has grown more friendly to client side rendering over the years and a few hacks exist to do pre-rendering of your app so search engines can see it, those techniques all leave something to be desired.

Fortunately, Meteor—the platform the Clever Beagle app and our mentorship program is based on—added support for server side rendering (SSR) last year. Instead of relying solely on client side rendering and hacks to serve up rendered HTML to search engines, now, it's possible to actually render HTML on the server-side first.

Why does that matter? Well, that rendered HTML is what Google uses to describe and rank your sites. The more accessible and visible that HTML is, then, the better.

Having worked with SSR previously in Meteor for a tutorial on our sibling site (as well as with a few CB mentees), I knew the gist of how to get it implemented. What I didn't know, though, was how to fix one of the glaring problems when implementing it: the flash of unstyled content (FOUC).

You've no doubt seen this before. When visiting a site, for a few split seconds you see the unstyled HTML show on the page and then after a few seconds see the styled version "snap in." Kind of like this:

The evil flash of unstyled content
The evil flash of unstyled content (FOUC)

Blech. No fun. Having researched this a bit, I knew that tools like styled-components—a library that helps you write per-component CSS for React—gave you a means for compiling your CSS on the server—just before you return your HTML, you compile your stylesheets and inject them into the header. The result? No FOUC!

This is all well and good but styled-components presents a unique dilemma: it's a bit cumbersome to migrate a large application to. If you start out with styled-components, it's a wonderful idea. A migration, though, has the potential to take awhile.

Looking to launch our Pupgrades offer in a few weeks, I started to bite my nails; a move to styled-components was potentially damning to staying on schedule.

Then it dawned on me: why not try something simpler. The problem is the flash of content, not that our CSS was particularly unwieldy. While migrating to styled-components would have been one way to solve the problem, it was far from the most economical. Thinking about the problem, a previous conversation with another developer came to mind. "Just make the body invisible until the content loads!" Well, duh.

With just three lines of code, the entire problem was solved. Three lines. Here's the distilled version:

[...]

if (Meteor.isClient) import './App.scss';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { ready: false }; // This one.
  }

  componentDidMount() {
    this.setState({ ready: true }); // This one.
  }

  render() {
    return (
      {/* This style tag below */}  
      <div className="App" style={{ visibility: this.state.ready ? 'visible' : 'hidden' }}>
        [...]
      </div>
    );
  }
}

[...]

In just five minutes of work (ten total if you consider the thinking that led to this), the problem was solved quite eloquently. No major rewrites. No significant change in process. A dirt simple, easy to "set and forget" solution.

FOUC be gone!
FOUC be gone!

When you're building your own products, always step back before committing to grandiose solutions. More often than not, there's always a far simpler, inexpensive way to solve your problem. Look for those first and only ratchet up your investment if you're certain it's necessary.

Want to get a heads up on Pupgrades when they launch?

Get on The Mailing List
Matt Michel
Using lodash to retrieve nested values

Using lodash to retrieve nested values

Cannot get property 'foo' of undefined

This is probably the most common class of error that I see in the JavaScript console, it happens when we try to access a property that doesn't exist. For example, if we have a couple of objects that look like this:

const idealObject = {
    name: {
        first: 'Scooby',
        last: 'Doo',
    },
    color: 'brown',
};

const subOptimalObject = {
    color: 'red',
};

const first1 = idealObject.name.last; // 'Doo'
const first2 = subOptimalObject.name.last; // Error: Cannot get property 'last' of undefined

We can get around this by checking for the existence of the fields we are referencing. If it exists then we return it, otherwise return the string 'n/a':

const first2 = subOptimalObject.name && subOptimalObject.name.last || 'n/a' ; 

This works but will get progressively messier with more complicated objects. This is where lodash comes in. We replace the above line with:

_.get(subOptimalObject, 'name.last', 'n/a');

Check out the lodash docs for lots of other cool time savers like this.


It Won't Be a Straight Line

​Excellent talk by Garrett Dimon, formerly of Sifter. In it, Garrett shares his story about what he learned from selling Sifter in light of a medical issue which prompted his left leg being amputated. He shares a number of anecdotes—some very humbling, heartwarming moments that are worth watching for—one of which stood out:

"Recurring revenue is really its own form of disability insurance. If you can work in recurring revenue, it can be a life changer."

Here, Garrett makes a point about how when he was unable to focus on Sifter, the recurring revenue it generated without his day-to-day involvement made a difficult experience far easier to contend with.

From one of his slides:

"Pain and discomfort are part of the process. Go right up to your limit. Push past it. Suffer a little. Then do it all over again."

A great point for anyone working on a product. Garrett makes a nod to hitting plateaus and having to have the discipline to push past them—an invaluable skill to have as the founder of a product.

Highly recommend putting this on and learning from Garrett and his experience.