by Anthony Ng

Deliberate Practice: What I learned from reading the classNames codebase

Becoming an Open Sourcerer

This is part of my plan for deliberate practice to improve as a developer. Take a look at this article to learn more.

In this article, we’ll look at a library called classNames (here’s the GitHub repository). classNames provides a simple API to construct class names in React. We’ll take a look at what it does, and what I learned by going through their repo.

How to use?

The classNames API is very simple. They have great examples in their README.md.

You can pass string arguments like so:

classNames also accept objects as arguments. If the value of the key is falsy (false, null, undefined, 0, NaN, empty string), classNames omits the value.

classNames also accepts arrays as arguments. Array arguments are recursively flattened and processed using the above rules. You can mix and match different types of arguments (strings, arrays, objects).

Usage with React

This package’s primary use case is to make React’s class name simpler to work with.

Without classNames, you might have used string manipulation to create React’s class names.

Now with the classNames package, it would look like this:

Common mistakes: “undefined” classnames

The most common mistake I see at work using classNames are undefined class names.

Remember that falsy values are ignored inside the classNames package.

Knowing this, we can update our classNames example to:

Different versions that you can opt into: Dedupe

There’s 2 issues that you might run into. Do you see them?

Luckily, classNames provides an opt-in version of its library for us to use, called dedupe.

That’s more like it. Note that dedupe is around 5x slower than the default classNames package. Use this only if needed.

Different versions that you can opt into: Bind

bind is another opt-in version of classNames. It’s meant to help us when we’re using CSS modules with React. But I find that the default classNames package does well with CSS modules.

Take a look at the README.md for more information.

Object.create(null)

It’s a best practice to use hasOwnProperty when iterating over an Object’s keys. You can check if the key belongs to the object, or is inherited.

We would use hasOwnProperty to get properties that belong to our created object.

Instead of using hasOwnProperty, we can create a new Object that inherits nothing!

But this also means methods that Objects inherit, such as toString will not exist on this new object.

Awesome documentation and great tests

Take a look through the source code of classNames. It’s littered with amazing comments and documentation.

Awesome documentation isn’t something that’s exclusive to open source projects.

Did you find a great code snippet that you used on your personal project?
Have you spent hours searching for the perfect StackOverflow answer? Include these links as comments in your code! It will save other developers (and the future you) time when figuring out what’s going on.

classNames has amazing documentation on their README.md. It has rich examples that show everything this package can do.

Documentation and comments are great. Yet, they can rot and become out of sync with what the code actually does. But tests don’t lie! Well-written tests will tell you everything that the package should be able to do and not do. If you’re new to a library, check out their tests to get a better understanding of the library.

apply/call

Knowing how to use JavaScript’s apply and call are great interview questions. But I rarely get to use them in the real world. Seeing it inside of the classNames package was a nice refresher of what it does.

apply and call basically do the same thing. It sets the this of the calling function.

For example,

The difference comes when you want to pass in arguments into the calling function. Let’s take a look at a function that takes in arguments.

Notice the small difference here. apply takes its arguments in an array (I remember it by remembering that apply and array both start with a). call takes its arguments provided individually, like a normal function would.

classNames uses apply to handle array arguments passed into it.

Don’t trust anything

Take a look at the code snippet below.

Why would we save the hasOwnProperty function to a variable? This is because we have to be defensive about the arguments given. We grab the hasOwnProperty from the Object.prototype. Let’s take a look at why.

That makes sense. But what if someone passed us an object like this:

Using the hasOwnProperty function from the Object.prototype is a safer alternative.

But note that even this isn’t foolproof. The below is still possible.

HTML entities

I always forget about HTML entities. I always look for a fancy image, but using HTML entities are well supported and can save you an HTTP request for an image.

Before you start scouring Google Images for assets, take a look at this chart to see if it has what you need.

Benchmark Performance

You don’t need to argue with your coworker any more about for-loops vs for-each loops! You can settle all disputes by seeing how it performs using benchmarking tools (such as jsPerf).

classNames is downloaded and used by many, and performance is of greatest concern. Performance differences are looked at before any pull requests are accepted.

Your personal projects might not be concerned with performance. But it is good to keep performance in mind. Take a couple of minutes to play around with jsPerf and set up your own tests.

travis.yml

Fancy badges from classNames README.md

Interested in getting adding fancy badges to your README.md? Check out this great egghead tutorial by Kent C. Dodds on starting your own Open Source project. It covers often-ignored topics, such as setting up Continuous Integration, using Semantic Release, publishing to npm, and more.

git blame, follow, history

Have you ever run into a certain line of code and was curious about how it came to be? Use the git blame feature from the Github website. It will tell you when it was written, who wrote it, and what commit it was from.

git blame

You can also view the history of a file and see how it evolved by using git history, located right next to git blame. Viewing all the commits shows you how a certain file evolved over time.

I recommend that you find an Open Source project that you like and use, and start contributing back to it. You can watch a repository and get notifications whenever there are any updates. You might not be ready to push code changes. But updating documentation or helping with other people’s issues are valuable as well.

watch

Thank you for reading this, and I hope you learned something new.