Introduction to React

About me

  • Scandinavia Online
    + Consulting / Training
  • JavaScript <3
  • eiriklv @ github
  • eiriklv @ twitter
  • http://vullum.io

Eirik Vullum

Do we really need yet another JavaScript framework?

What is React, and what does it actually solve?

Open Source

A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES

What React isn't

  • MVC framework
  • Templating library
  • 2-way data-binding

Basically just the V in MCV

  • But not really
  • Another way of thinking about stateful UI
  • Facilitates some interesting patterns
  • Powerful abstractions
  • Supports universal rendering OOTB

Stateful UI's

  • Can be a pain to reason about
  • Imperative mutative API's </3

Imperative API

Application

DOM

mutate state

mutate state

read state

read state

:(

Declarative UI's

  • Describe your UI at any point in time
  • Changes => toss everything => re-render (conceptually)
  • Tell your UI what to look like
    - not how to get there

Declarative API

React

(virtual DOM)

(JavaScript)

DOM

(browser)

Application

state

patch DOM

patch DOM

props

change

This is a powerful abstraction

but what about perf..?

How this is viable

  • Virtual DOM (JavaScript)
  • Efficient diffing algorithm
  • No state kept in DOM

Using React to make awesome stuff

A Closer Look

var HelloWorldComponent = React.createClass({
  
  render: function() {
    return (
      <div className="some-style">
        <p>Hello World</p>
      </div>
    );
  }

});

Components

 JSX

It's all JavaScript

  • Just a transform (like CoffeeScript)
  • No crippled templating language
  • Separation of concerns (not technologies)
  • Does not emulate HTML 100% (some gotchas)
  • Optional (but highly encouraged)
var HelloWorldComponent = React.createClass({
  
  render: function() {
    return (
      React.createElement("div", { className: "some-style" },
        React.createElement("p", null, "Hello World");
      );
    );
  }

});

Without JSX

ReactDOM.render(
  <HelloWorldComponent/>,
  document.querySelector('#mountNode');
);

Mouting into the DOM

That's all nice

But what about state, changes and everything that makes my app dynamic?

Props and State

Using Props

var HelloWorldComponent = React.createClass({
  
  render: function() {
    return (
      <div className="some-style">
        <p>{this.props.greeting}</p>
      </div>
    );
  }

});

Passing Props

<HelloWorldComponent greeting='Hello World' />
<HelloWorldComponent greeting={'Hello ' + 'World'} /> 
<HelloWorldComponent greeting={getGreeting()} />

State

Initial State

var HelloWorldComponent = React.createClass({
  
  getInitialState: function() {
    return {
      count: 0
    };
  },

  render: function() {
    return (
      <div className="some-style">
        <p>{this.props.greeting}</p>
        <p>{this.state.count}</p>
      </div>
    );
  }

});

Updating State

var HelloWorldComponent = React.createClass({
  
  getInitialState: function() {
    return {
      count: 0
    };
  },

  handleClick: function () {
    this.setState({
      count: this.state.count + 1
    });
  },

  render: ...
});

Events

  • Synthetic
  • Declarative

Event Handlers

var HelloWorldComponent = React.createClass({
  getInitialState: ...

  handleClick: ...

  render: function() {
    return (
      <div onClick={this.handleClick} className="some-style">
        <p>{this.props.greeting}</p>
        <p>{this.state.count}</p>
      </div>
    );
  }
});

Event Handling

var HelloWorldComponent = React.createClass({
  getInitialState: ...

  handleClick: function (event) {
    event.preventDefault();

    this.setState({
      count: this.state.count + 1
    });
  },

  render: ...
});

Event Types

DOM Events

var Box = React.createClass({
  
  getInitialState: ...

  handleResize: function(e) {
    this.setState({
      windowWidth: window.innerWidth
    });
  },

  componentDidMount: function() {
    window.addEventListener('resize', this.handleResize);
  },

  componentWillUnmount: function() {
    window.removeEventListener('resize', this.handleResize);
  },

  render: function() {
    return <div>Current window width: {this.state.windowWidth}</div>;
  }

});

Lifecycle Methods

Lifecycle Methods

First render call

Subsequent

render calls

Updating state

getDefaultProps

getInitialState

componentWillMount

render

componentDidMount

componentWillReceiveProps

shouldComponentUpdate

componentWillUpdate

render

componentDidUpdate

shouldComponentUpdate

componentWillUpdate

render

componentDidUpdate

Lifecycle Methods

Being removed from the DOM

componentWillUnmount

Lifecycle Methods

var MyComponent = React.createClass({
  // lifecycle methods
  getDefaultProps: ...
  getInitialState: ...
  componentWillMount: ...
  componentDidMount: ...
  componentDidUpdate: ...
  componentWillReceiveProps: ...
  componentWillUnmount: ...
  shouldComponentUpdate: ...

  // custom methods
  handleThis: ...
  handleThat: ...
  computeThis: ...
  computeThat: ...

  // rendering
  render: ...
});

Mixins

Mixins

var MyComponent = React.createClass({
  getInitialState: function() {
    return { seconds: 0 };
  },
    
  componentDidMount: function() {
    this.interval = setInterval(this.tick, 1000);
  },
    
  componentWillUnmount: function() {
    clearInterval(this.interval);
  }

  tick: function() {
    this.setState({
      seconds: this.state.seconds + 1
    });
  }
    
  render: function() {
    return <div>Seconds run: {this.state.seconds}</div>
  }
});

Mixins

var SetIntervalMixin = {    
  componentDidMount: function() {
    this._intervals = [];
  },
    
  componentWillUnmount: function() {
    this._intervals.forEach(function(interval) {
      clearInterval(interval);
    });
  },

  setInterval: function() {
    this._intervals.push(setInterval.apply(null, arguments));
  }
});

Mixins

var MyComponent = React.createClass({
  mixins: [SetIntervalMixin],

  getInitialState: function() {
    return { seconds: 0 };
  },
    
  componentDidMount: function() {
    this.setInterval(this.tick, 1000);
  },

  tick: function() {
    this.setState({
      seconds: this.state.seconds + 1
    });
  }
    
  render: function() {
    return <div>Seconds run: {this.state.seconds}</div>
  }
});

Composition

Composition

<ChatApp />

<FriendList />

<MessageList />

<Friend />

<Friend />

<Friend />

<Friend />

<Message />

<Message />

<Message />

<MessageInput />

Composition

var ChatApp = React.createClass({
  ...

  render: function() {
    return (
      <div>
        <FriendList friends={this.state.friends} />
        <MessageList messages={this.state.messages} />
        <MessageInput handleInput={this.handleInput} />
      </div>
    );
  }

});

Composition

var MessageList = React.createClass({
  render: function() {
    var messages = this.props.messages.map(function(message) {
      return (
        <Message
          content: message.content,
          from: message.from
        />
      );
    });

    return <ul>{messages}</ul>;
  }
});

Composition

var Message = React.createClass({
  render: function() {
    return (
      <li>
        <span>From: {this.props.from}</span>
        <span>Content: {this.props.content}</span>
      </li>
    );
  }
});

Composition

But where should i put all my state and stuff..?

Composition

<ChatApp />

<FriendList />

<MessageList />

<Friend />

<Friend />

<Friend />

<Friend />

<Message />

<Message />

<Message />

<MessageInput />

this.state.friends

this.state.messages

this.handleInput

this.props.friends

this.props.messages

this.props.handleInput

Data Flow

So won't this turn into a mess when my app grows in complexity..?

Flux / Redux

Unidirectional Data Flow

Flux

Flux

Integration with existing libraries

Backbone

backbone-react-component

react-backbone

backbone-react-component

// create component
var MyComponent = React.createClass({
  mixins: [Backbone.React.Component.mixin],
  render: function () {
    return <div>{this.state.model.foo}</div>;
  }
});

// create model
var model = new Backbone.Model({foo: 'bar'});

// render component and pass model as prop
React.render(<MyComponent model={model} />, document.body);

// update the UI
model.set('foo', 'Hello world!');

Angular

ngReact

ngReact

link: function(scope, elem, attrs) {
  //...

  scope.$watch(attrs.props, function() {
    React.render(<MyComponent scope={scope} />, elem[0]);
  }, true);

  scope.$on('$destroy', function() {
    React.unmountComponentAtNode(elem[0]);
  });
}

jQuery

Interacting with the DOM through

  • refs
  • ReactDOM.findDOMNode

jQuery

var MyComponent = React.createClass({

  componentDidMount: function() {
    var node = React.findDOMNode(this.refs.myContainer);
    var $node = $(node);
    // manipulate the node
  },

  componentDidUpdate: function() {
    var node = React.findDOMNode(this.refs.myContainer);
    var $node = $(node);
    // manipulate the node
  },

  componentWillUnmount: function() {
    // clean up if necessary
  },

  render: function() {
    return <div ref='myContainer'>Hello World</div>;
  }

});

om -  ClojureScript

(ns example
  (:require [om.core :as om]
            [om.dom :as dom]))

(defn widget [data owner]
  (reify
    om/IRender
    (render [this]
      (dom/h1 nil (:text data)))))

(om/root widget {:text "Hello world!"}
  {:target (. js/document (getElementById "my-app"))})

Targeting other platforms

React Canvas

React Canvas

<Surface>
<Layer>
<Group>
<Text>
<Image>
<ListView>

<div>
<span>
<p>
<ul>
<li>
<table>

vs.

React Native

React Native

<Image>
<ListView>
<MapView>
<Navigator>
<ScrollView>
<Text>
<TextInput>
<View>
<WebView>

...

<div>
<span>
<p>
<ul>
<li>
<table>

vs.

Other Targets

  • Desktop
  • Terminal (blessed)
  • ThreeJS

Pretty much anything!

Learn once - Write anywhere

Write once - Deploy anywhere

Development Tools

Real world examples

  • Facebook
  • Netflix
  • Instagram
  • SOL
  • Flipboard

Takeaways

  • Different way of thinking
  • It's just JavaScript, and it's really fast
  • Stop fighting imperative stateful API's and build declarative apps with React
  • Composition is king
  • There has never been a better time to be a JS dev

Questions?

Shameless plugs

React Workshop - May 6-7.

NDC Training

NDC

Building Isomorphic Apps in JavaScript

Talk - June 19.