Front-end Components at FindHotel

In this post we'll take a look at code re-use at a component level in our front-end at Find Hotel.

Our ultimate goal is to create as many re-usable pieces of functionality that we can use in different places of our website. We aim for a very flexible setup because we A/B test aggressively. We also want to make sure that each of the small pieces of functionality behave predictably no matter where there are on the page, as we might move them around, remove or alter them during an A/B test.

Let's start by taking a look at some of the components we have built for our website.

Search Page

searchresults

Hotel Detail Page

offers

As you can see some components are re-used in and across pages, such as the price, the rate information and stars. Obviously breaking functionality into small components isn't new concept, we've been doing this for years by using CSS classes such as .price and perhaps writing macros or partials in your template language of choice.

This always did involve jumping through different layers of your application architecture though, and as your application grows, making sure you updated all layers (template, JavaScript and CSS) becomes cumbersome. More importantly this separation opens the door to creating legacy / orphaned code because it becomes scary to remove a CSS declaration or macro if you're not sure if other parts of the system still use it.

React JS + Redux + Webpack + CSS Modules (+ a lot more for later)

We decided to use Facebook's React as our View Layer, and Redux as the framework for binding the views to our data stores.

The main selling point of React's internal use of a virtual DOM is well documented, it allows the developer to re-render a component with new data and React will figure out how to update the actual DOM. This in itself is refreshing, everything about a component becomes predictable, feed it certain data and it will always render the same, whether it's during initialization or run-time updates.

The high level of granularity allows for easy re-use of concepts. Imagine you'd want to show a price and tax breakdown when hovering a price anywhere in the website. In this case you need to only update the Price component and the new functionality will be available everywhere the component is used.

We use Webpack for dependency management in our front-end; it's not unlike requireJS but has the concept of 'loaders' which act as middleware that can transform the contents of the modules while you are loading them. We'll see soon how we use this to our advantage.

Since we also require CSS within our React components, everything that's required for a component to render and function is in one place, it becomes much safer to adjust or remove a component.

Combining this with a unilateral data flow architecture such as Flux (we use Redux, a derivative) allows for a refreshingly predictable application architecture, easing the pain of development and testing, which I'll gladly explain in detail in a next blog post, stay tuned!

Code

Let's look at a simplified implementation of our Price component. In case things look a little alien, we are using CoffeeScript combined with React's JSX to define our components.

In case you're not familiar with React, the following renders a custom React component in a container with id root:

main.cjsx

React = require 'react'
ReactDom = require 'react-dom'
Price = require 'components/price.cjsx'
ReactDom.render <Price value={123} />, document.getElementById 'root'

The last line instantiates our Price component, the attributes (props in React terms) allow us to customize the behavior of it. In this case we simply pass it a value of 123.

Let's look at the implementation:

components/price.cjsx

React = require 'react'
styles = require 'components/price.sass'

module.exports = React.createClass
 render: ->
   <span className={styles.price}>
     {@props.value}
   </span>

This can be considered the class definition of the component, our React component has only one method for now, rendering the price as a with the 'value' as a text node. The Flux / Redux concept of unilateral data flow dictates that presentational components like this receive properties from a parent, in this case the @props.value. In our architecture no components can load data themselves, all data is passed from parent to child so you can develop and test each of these components in an isolated manner.

Much has been written about these powerful concepts, I recommend the following resources:

Flux

This Medium post

Getting started with Redux

Modularization of styles with CSS Modules

Ok, so now we know what components look like, let's look at styling. Re-usable CSS has been a long-standing problem, since all CSS declarations are considered global, the chance of conflicts gets higher as your website grows in complexity, and when you're A/B testing aggressively this problem is compounded.

Luckily there's a couple of solutions. First we've used naming conventions such as BEM to reduce the risk of conflicts, however this is quite verbose and requires highly disciplined efforts on the developer's side.

React itself has 'pioneered' the return of inline styles, which we use for a lot of 'business logic' styles like showing and hiding certain elements. While we see the benefit of inline styles, it also takes away some of the things that make CSS so powerful (cascading, etc). Additionally, at the time we started development it had some serious limitations such as the lack of Media Query support - and frankly some of the SASS functions such as darken and lighten are too useful for us to give up for the moment.

To eliminate the issue of styling conflicts we've adopted the CSS Modules pattern, which is supported by Webpack's Style Loader by default.

With the loaders we use in Webpack we can write the following SASS:

:local(.price)
 color: red

The :local() function is extremely handy as it converts the classname into a hash that is guaranteed to be unique, preventing conflicts.

Since we use the hashed classname in our render function:

render: ->
   <span className={styles.price}>
     {@props.value}
   </span>

the resulting HTML references the mangled classname:

<span class="_2HShB-zr-IrjKOj8-Fq-5A">123</span>

This does introduce some complexities with automated testing as these classnames changes with each deploy, but luckily there's another project that exports the mapping so you can refer to the original classnames.

More about our end-to-end testing approach in another blog post.

In case you're wondering we use the following processors in our webpack config:

module:
  loaders: [
    {
      test: /\.sass$/,
      loader: 'style!css!postcss!sass'
    }
  ]

The processors are called from right to left so the SASS loader first converts sass into regular CSS, then pipes it to postCSS to which is used to augment the resulting CSS with necessary browser prefixes (like Compass would normally do) and producing RTL stylesheets.

Using the above approaches we can guarantee that including and using the Price component doesn't have side effects, and if we decide to not use it, the webpack resolver will not include any of the related assets in to the build, effectively removing dead code on a module level.

Unit Testing

As I mentioned it's very important to us that our components behave predictably, so we perform unit testing on component level. When the unilateral data flow of Flux / Redux is implemented diligently there is (almost) no need for mocking in Unit Tests.

With React and pure components this becomes super easy as you can take a component at any level and render it with certain properties, and expect it to emit Redux actions, you can exercise an isolated component without having to bother or know anything about the surrounding system.

Here is an extremely simplified unit test for the price component, we render the Price component and assert whether the resulting HTML is what we expect:

describe 'Currency Rendering', ->
  beforeEach ->
     props =
       value: 123
       showDecimals: true
       currency: 'EUR'

     # omitted for simplicity, stores rendered DOM element as @domElement
     renderPrice(props)

  it 'renders with the correct currency', ->
    expect(@domElement.textContent).to.equal '€123.00'

This concludes a brief look at the smallest units of our code-base, re-usable components. Stay tuned for more articles about other parts of our front-end architecture, feel free to contact me if there are specific topics you're interested in!