REACT.JS

REMEMBER TO GIVE IT 5 MINUTES

"The faster you react, the less you think."

- Jason Fried

https://signalvnoise.com/posts/3124-give-it-five-minutes

DON'T BE THIS GUY

PRODUCTION

WHAT REACT IS NOT

SJRS / RJS

  • Server Javascript Response
    • Server responds back with JS that is eval'd
  • Ruby Javascript
    • Javascript w/ embed Ruby

Reactive-Extensions

 RxJS

  • JS library by Microsoft
  • Observables + LINQ + Schedulers
  • Asynchronous data stream handling

More than a template.

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>

TEMPLATES ENCOURAGE POOR SEPARATION OF CONCERNS

TEMPLATES SEPARATE TECHNOLOGIES NOT CONCERNS

MARKUP AND DISPLAY LOGIC ARE INEVITABLY TIGHTLY COUPLED 

It's not MVC.

SUPER CHARGED RENDERING ENGINE

React is more like a declarative jQuery...

...without the jQuery.

No dependencies.

Just Javascript.

React is a framework for building UIs.

React is a framework for building declarative UIs.

...but why?

// Initialize stuff on load
$(document).ready(function() {
  initializeABunchOfThings();
});

// Scattered Events Everywhere
$('#button-1').on('click', function() {
  $(this).toggle('active')
});

$('#button-2').on('click', function() {
  if ($('#button-1').hasClass('active') {
    doSomething();
  } else {
    doAnotherThing();
  }
});

"I need a sane way to build composite stateful interfaces with minimal mental overhead."

- @dan_abramov

Front End, Stampsy

Building a

UI is hard.

Building a UI

Managing state is hard.

State changing

over time is evil.

"Programs that use state in a haphazard way are very difficult to understand. For example, if the state is visible throughout the whole program, then it can be assigned anywhere. The only way to reason is to consider the whole program at once." 

Concepts, Techniques, and Models of Computer Programming

Managing complexity is hard.

React helps keep your UI predictable...

...with 2 Silver Bullets.

1. Composition

  • Combine simple functions to build complex ones
  • Strict Interfaces

2. Idempotence

  • Repeatable, with the same result
  • Easy to predict output based on inputs
  • Immutability gives this to us for free
  • Minimize mutating state
ids = [1, 2, 3, 4, 5]

ids.remove(1)
# => [2, 3, 4, 5]

ids.remove(1)
# => [2, 3, 4, 5]

React lets you easily push your data into your UI.

DATA

IN

VIRTUAL DOM

OUT

...tell me more!

BASIC REACT COMPONENT

  • STATE
  • PROPS
  • RENDER
/** @jsx React.DOM */
var HelloMessage = React.createClass({
  displayName: 'HelloMessage',
  render: function() {
    return (
      React.DOM.div(null, "Hello ", this.props.name)
    );
  }
});

React.renderComponent(HelloMessage({
  name: "John"
}), document.getElementById('content'));

2. CHILD ELEMENTS

1. NODE PROPERIES
{ id: 'name' }

3. PROPS

4. COMPONENT

HelloMessage Component

/** @jsx React.DOM */
var HelloMessage = React.createClass({
  render: function() {
    return (
      <div>
        Hello {this.props.name}
      </div>
    );
  }
});

React.renderComponent(<HelloMessage name="John" />, mountNode);

HelloMessage Component (JSX)

<div id="content">
  <div data-reactid=".0">
    <span data-reactid=".0.0">Hello </span>
    <span data-reactid=".0.1">John</span>
  </div>
</div>

HTML OUTPUT

Component Lifecycle Event Hooks

  • componentWillMount()
  • componentDidMount()
  • componentWillReceiveProps(nextProps)
  • shouldComponentUpdate(nextProps, nextState)
  • componentWillUpdate(nextProps, nextState)
  • componentDidUpdate(prevProps, prevState)
  • componentWillUnmount()
/** @jsx React.DOM */
var Timer = React.createClass({
  getInitialState: function() {
    return {
      secondsElapsed: 0
    };
  },
  tick: function() {
    this.setState({
      secondsElapsed: this.state.secondsElapsed + 1
    });
  },
  componentDidMount: function() {
    this.interval = setInterval(this.tick, 1000);
  },
  componentWillUnmount: function() {
    clearInterval(this.interval);
  },
  render: function() {
    return (
      <div>
        Seconds Elapsed: {this.state.secondsElapsed}
      </div>
    );
  }
});

React.renderComponent(<Timer />, mountNode);

Timer Component

Seconds Elapsed: 9000

getInitialState: function() {
  return {
    secondsElapsed: 0
  };
},

SET THE INITIAL STATE

ON COMPONENT MOUNT

// this.tick();

tick: function() {
  this.setState({
    secondsElapsed: this.state.secondsElapsed + 1
  });
},

DEFINE A FUNCTION

UPDATE THE STATE

componentDidMount: function() {
  this.interval = setInterval(this.tick, 1000);
},

SET THE TIMER

PASSING THE TICK FUNCTION

ON MOUNT

componentWillUnmount: function() {
  clearInterval(this.interval);
},

REMOVE THE TIMER BEFORE

COMPONENT IS UNMOUNTED

render: function() {
  return (
    <div>
      Seconds Elapsed: {this.state.secondsElapsed}
    </div>
  );
}

DEFINE THE MARKUP IN RENDER

// JSX
React.renderComponent(<Timer />, mountNode);

// JS
React.renderComponent(Timer(null), mountNode);

RENDER THE COMPONENT

/** @jsx React.DOM */

//  props:
//    currentLat, count, modules
var LiModules = React.createClass({
  render: function() {
    var moduleNodes = this.props.modules.map(function(module, index) {
      return (
        <LiModule
          id={module.id}
          name={module.name}
          activities={module.activities}
          currentLat={this.props.currentLat}
          polling={this.props.polling}
          key={index}
        />
        );
    }.bind(this));

    return (
      <div className="launchpad-module-groups">
        <div className="box instance-group">
          <LiModulesHeader count={this.props.count} />
          
          <div className="box-content scrollable instances launchpad-modules">
            {moduleNodes}
          </div>
        </div>
      </div>
      );
  }
});

NESTING

Typical SPA

Architecture

PROPS

UNIDIRECTIONAL FLOW

React Architecture

Components describe the DOM at any point in time.

Upon state change, React will RERENDER.

VIRTUAL DOM

DOM represented in Javascript

SO REPRESENTING THE DOM IN JS... CALL ME MAYBE THIS IS CRAZY.

...wait! Is this the "Shadow DOM"?

No.

Virtual DOM

  • The real DOM is SLOW.
  • Javascript is actually FAST.
  • Server side rendering (Node doesn't need a DOM API like PhantomJS)
  • Allow React to provide an easy way to manipulate the DOM and Events

VIRTUAL DOM DIFFING

  • VERY efficient
    • O(n^3) problem into a O(n) one.

  • No watching or dirty-checking objects
  • New DOM tree compared to the old
    • Only minimal changes to DOM applied
    • No unnecessary build/teardown 
    • Scrolling doesn't break
    • Inputs don't break either

REVIEW

  • Components describe the DOM at any point in time
  • VIRTUAL DOM
  • High performance
    • less jank, 60fps even in non JIT UI-WebView
    • Minimal DOM updates
  • Batches reads and writes
  • API is very small, (get the gist in like ~30 minutes)
  • Doesn't make any assumptions
    • use your own datastores
    • works with dom libraries/jquery
var BottleGame = React.createClass({
  getInitialState: function () {
    return {
      bottles: 99
    };
  },

  render: function() {
    var bottles = this.state.bottles,
        warning = (this.state.bottles < 10 ? 'warning' : false);

    return (
      <div>
        <p className={warning}>Bottles: {text}</p>

        {bottles > 0 ?
          <button onClick={this.handleTakeClick}>Take one down and pass it around</button> :
          <button onClick={this.handleAgainClick}>Start again</button>
        }
      </div>
    );
  },

  handleTakeClick: function () {
    this.setState({ bottles: this.state.bottles - 1 });
  },

  handleAgainClick: function () {
    this.setState({ bottles: 99 });
  }
});

React.renderComponent(BottleGame(), document.body);

BottleGame

React Component

getInitialState: function () {
  return {
    bottles: 99
  };
},

INITIALIZE STATE

handleTakeClick: function () {
  this.setState({ bottles: this.state.bottles - 1 });
},

handleAgainClick: function () {
  this.setState({ bottles: 99 });
}

DEFINE HANDLERS

TO UPDATE STATE

render: function() {
  var bottles = this.state.bottles,
      warning = (this.state.bottles < 10 ? 'warning' : false);

  return (
    <div>
      <p className={warning}>Bottles: {text}</p>

      {bottles > 0 ?
        <button onClick={this.handleTakeClick}>
          Take one down and pass it around
        </button> :
        <button onClick={this.handleAgainClick}>
          Start again
        </button>
      }
    </div>
  );
},

RENDER MARKUP

HOW WOULD IT LOOK IN JQUERY OR BACKBONE?

var para = $('#bottles'),
    take = $('#take'),
    again = $('#again'),
    bottles = 99;

function update() {
  if (bottles > 0) {
    take.show();
    again.hide();
  } else {
    take.hide();
    again.show();
  }
  
  if (bottles > 1) {
    para.text(bottles + ' bottles of beer on the wall');
  } else if (bottles === 1) {
    para.text('One bottle of beer on the wall');   
  } else {
    para.text('No bottles of beer on the wall');
  }
}

take.click(function () {
  bottles--;
  update();    
});

again.click(function () {
  bottles = 99;
  update()
});

update();
<p id="bottles">9 bottles...</p>
<button id="take">Take one...</button>
<button id="again">Start...</button>

BottleGame jQuery

var BottleGameView = Backbone.View.extend({
  el: '#container',

  events: {
    'click button.take': 'takeBottle',
    'click button.again': 'startAgain'
  },

  initialize: function () {
    this.model = new Backbone.Model({ bottles: 99 });
    this.render();
    this.listenTo(this.model, 'change:bottles', this.handleBottlesChange);
  },
  
  handleBottlesChange: function () {
    var bottles = this.model.get('bottles');
    if (bottles > 0) {
      this.$take.show();
      this.$again.hide();
    } else {
      this.$take.hide();
      this.$again.show();
    }
      
    if (bottles > 1) {
      this.$para.text(bottles + ' bottles of beer on the wall');
    } else if (bottles === 1) {
      this.$para.text('One bottle of beer on the wall');   
    } else {
      this.$para.text('No bottles of beer on the wall');
    }
  },

  render: function () {
    var template = _.template($('#bottles_template').html(), this.model.attributes);
    this.$el.html(template);
    
    this.$para = this.$el.find('.bottles');
    this.$take = this.$el.find('.take');
    this.$again = this.$el.find('.again'); 
    
    this.$again.hide();
  },

  takeBottle: function () {
    this.model.set('bottles', this.model.get('bottles') - 1);
  },
  
  startAgain: function () {
    this.model.set('bottles', 99);
  }
});

new BottleGameView();

BottleGame Backbone

var BottleGame = React.createClass({
  getInitialState: function () {
    return {
      bottles: 99
    };
  },

  render: function() {
    var bottles = this.state.bottles,
        warning = (this.state.bottles < 10 ? 'warning' : false);

    return (
      <div>
        <p className={warning}>Bottles: {text}</p>

        {bottles > 0 ?
          <button onClick={this.handleTakeClick}>Take one down and pass it around</button> :
          <button onClick={this.handleAgainClick}>Start again</button>
        }
      </div>
    );
  },

  handleTakeClick: function () {
    this.setState({ bottles: this.state.bottles - 1 });
  },

  handleAgainClick: function () {
    this.setState({ bottles: 99 });
  }
});

React.renderComponent(BottleGame(), document.body);

BottleGame React Component

POSSIBILITIES

  • Not a big commitment
    • Plays nice with Backbone, Angular, etc.
  • Components can map to PSDs, working w/ designers
  • Playback/Repeat/Undo for QA
  • Rendering
    • SVG, VML, CANVAS
    • Web Worker

Other Stuff

  • Flux
  • Om, Reagent
    • ClojureScript,
    • Request Animation Frame for DOM updates

CONS

  • Its Javascript... so can lead to callbacks on callbacks.
  • Render must return a single element
  • You have to be explicit.
  • Works best with hierarchical data
  • Unidirectional

RE'ACTING

THE PORTAL

      ACTING

BACKBONE

Usecase

  • Hierarchical page
    • Modules
      • Sub modules
  • Complexity was rising
  • Constant polling
    • Backbone rerenders were expensive

NESTED COMPONENTS

REACT COMPONENTS

Thanks!

Special Thanks

deck

By vestige