37
How we use React.js A case study in completely re-implementing your UI in one month Iron Yard Crystal City - Tuesday April 7, 2015

React.js at Cortex

Embed Size (px)

Citation preview

How we use React.jsA case study in completely re-implementing your UI in one month

Iron Yard Crystal City - Tuesday April 7, 2015

Hi, I’m Geoff• CTO and Cofounder at Cortex

• Began career as an analyst and project

manager

• Began development as a hobby

(interested in baseball stats)

• Fell in love with Ruby after initial

flirtation with PHP

• Started Five Tool Development, a web

development agency

What does Cortex do?

• Our application provides commercial building operators and

engineers with minute-to-minute recommendations about energy use

and live data from hundreds of sensors and meters

• Our first building saved half a million dollars in energy costs during

their first year using Cortex

• 95% of our customer use is through mobile apps. We deliver a

responsive web application in a WebView container to iOS and

Android

What we’re going to talk about

• Super-fast overview of what React is

• How we used React to dramatically improve Cortex

• Even faster overview of Flux

• Questions (feel free to stop me any time)

Problem

Building engineers frequently work in spaces with no network

connectivity.

They expect to be able to get their data everywhere.

Complication

Need to support three platforms (web, iOS, Android) with limited

resources.

We have already invested hundreds of hours in a first-class HTML-

based interface.

The Plan

Turn our webapp into a persistently-open single-page application

(SPA). Users will be on the same page all the time, so the application

will never reload in the browser.

Our application will fetch and cache data when the network is available,

and present it even when the network is unavailable.

What is MVC?

Source: Google (https://developer.chrome.com/apps/app_frameworks)

Lots of JavaScript MVC Frameworks

Compared to Ruby, JavaScript is the

Wild (Wild) West.

Space is less mature, but filled with really

interesting new innovations and ideas.

Cutting-edge solutions, but not a lot of

consensus around which are the best yet.

Source: James T. West

Backbone

• Created by Jeremy Ashkenas from the New

York Times visualizations group

(he built Coffeescript, Underscore.js)

• Early pioneer in JavaScript MVC frameworks

• Focus on data sync between browser and

server-based API, leaves view/presentation

layer up to developer

Ember• Created by Yehuda Katz (of Rails- and jQuery-core fame) and Tom Dale

• Uses two-way data binding

• Very opinionated, does things “the Ember Way”

• Explicitly for single-page apps

• Has awesome routing

• Liked by many Rails developers

Angular• Developed at Google

• Uses two-way data binding

• Less opinionated than Ember, which means it

is more flexible, but also requires you to do

more heavy lifting yourself

• Not just for SPAs

• Extends HTML with custom attributes

React• Developed at Facebook

• Unidirectional flow of data (one-way binding)

• Re-renders everything when something

changes, uses a “virtual DOM” to calculate

differences

• Can be implemented in small pieces, does not

require full-scale use across application

• Younger than previous options, rapidly growing

With or without you

(by “you”, I mean a network)

• Cache data in browser using

localStorage API

• Always be on the same page,

never reload the page due to user

clicks

• Give friendly messages when

network is unavailableSource: The Edge

The need for speed

• Context changes should be snappy

• Pre-fetch data the user will likely

want and put it into localStorage

before the user requests it

• Minimize delays in rendering UI

components due to cascading

changes with asynchronous

execution Source: Miramar Naval Air Station, aka “Top Gun”

So how does React work?

• React’s building blocks are Components

• Components are JavaScript objects tasked with outputting HTML

• Only requirement is a render() function that returns HTML

• Components can maintain their own internal state

A dumb component

<div class=“commentBox”>

Hello, World! I am a CommentBox.

</div>

var CommentBox = React.createClass({

render: function() {

return (

<div className="commentBox">

Hello, world! I am a CommentBox.

</div>

);

}

});

React.createElement(CommentBox,{});

A slightly less dumb component

<div class=“greeting”>

Welcome, Homer

</div>

var Greeting = React.createClass({

render: function() {

return (

<div className="greeting">

Welcome, { this.props.username }

</div>

);

}

});

React.createElement(

Greeting,

{ username: “Homer” }

);

A slightly smart componentvar UserList = React.createComponent({

getInitialState: function() {

return { users: [] };

},

users: function() {

return _.map(

this.state.users,

function(user) {

return (

<li className="user">{ user.name }</li>

);

},

)

},

render: function() {

return (

<ul className="users">

{ this.users() }

</ul>

);

}

});

# with no users (at initialization)

<ul class=“users”></ul>

# with one user (state changed)

<ul class=“users”>

<li class=“user”>Geoff</li>

</ul>

# with two more users and one removed

<ul class=“users”>

<li class=“user”>James</li>

<li class=“user”>Su</li>

</ul>

Smarter is not better

• Props are immutable, easier to track

• Favor props over state whenever possible

• Make component functions as dead-simple as you can

• Dumber components are easier to test, and likely faster to render

Components nest

• Any complicated element or repeated set of elements should be its

own component

• When the parent re-renders, React figures out whether or not the

children should remain, be updated, or be removed

• Components can be passed in as props to a component or returned

from a function in a component

• React can render an array of components

React lifecycle• React offers a number of “hooks” that allow you to trigger events at

certain points in the Component’s lifecycle

• Examples of lifecycle events: when a component renders, when it gets

updated state, when it is removed from the DOM

• Uses: start a timer, listen for UI events, tie in jQuery or other libraries

after a component is rendered, then remove those timers/listeners when

the component is unmounted

• Cortex uses lifecycle to trigger chart drawing with d3 and updates every

30 seconds

Use functions liberally

• Functions are fast (mostly)

• Keep logic in functions outside of render().

render() should look as much like HTML as

possible.

• Just like your Ruby code, many short

methods with limited arguments makes for

cleaner, easier to test code than one

massive God method that does all the

decision-making Source: Arkansas

Shared behavior

• React components share behavior through Mixins

• Mixins don’t provide the same inheritance or composition capabilities

as Ruby (no overriding methods or implementations)

• Example of shared behavior: all cards that collapse or expand have

the same collapse/expand mixin

Lots of components!Page ->

Dashboard ->

Navigation ->

Navigation Item(s)

Card Collection ->

FlashMessage(s)

Card(s) ->

Card Header

Card Chart

Card Tab

Lots of components!

Let’s take a look at the components in Cortex

React tips

• Don’t manipulate HTML yourself!

React can’t keep track of HTML changes you make after rendering. If

you’re tempted to use jQuery or another library to alter HTML, figure

out a way to do it through the component’s props and state.

• Don’t be afraid to break components into smaller sub-components

More React tips

• Components can communicate with their parents through functions.

Pass a function from the parent to the child as a prop, then the child

can call it and send a message to the parent. Super useful, but don’t

overdo it!

• Start small. One of the best things about React is that you can

implement it in little pieces in your UI as you find opportunities to use

it.

What about global state?

• In our first attempt at using React, we had a top-level “Page”

component that rendered all the various views inside of itself. The

Page was responsible for maintaining global state such as the date

being viewed, the current building, and the desired page.

• Components would pass the global state as properties to children

who often did nothing but just pass that state on again to their own

children

• It felt messy. It was!

Enter Flux

• Flux is Facebook’s answer to storing global state in React apps

• Flux data is kept in “stores”, which announce changes to React

components, which can update stores with “actions”

• A dispatcher ensures that updates that depend on other updates

don’t get deadlocked

Flux Architecture

Source: Facebook

What did we lose?

Here’s how the application has gotten worse:

• Application is considerably more complicated than when it was just

Rails and jQuery (this would have happened with any of the other JS

frameworks)

• More places things can break in our code

• React is much harder to test well than Rails with Capybara

• Some co-mingling of business logic and our presentation layer

What did we accomplish?

Here’s how the application has improved:

• Application is much faster from the user’s perspective

• Feels more “native”, with no reloads and quick changes after clicks

• App is useful when the network isn't available

• Ruby is responsible for our server-side work, and JavaScript owns

the browser and presentation layer

Further reading

• React.js: https://facebook.github.io/react/

• Flux: https://facebook.github.io/flux/

• Marty.js (a great Flux implementation): http://martyjs.org/

• React-rails gem

(mount React components in views, compile JSX in asset pipeline):

https://github.com/reactjs/react-rails

Questions?

I’m happy to answer questions about:

• React and Flux

• Ruby or Rails

• Cortex

• Working as a developer

• The awful state of the Boston Red Sox’ starting rotation

Thanks!

Feel free to get in touch!

twitter: @geoffharcourt

github: geoffharcourt

email: [email protected]