React

JS

By Facebook

I will discuss about

  • Components and JSX
  • Virtual DOM
  • Flux
  • Redux and hot loading

React

  • UI library developed at Facebook
  • Not yet another JS Framework
  • Allows us to create stateful reusable UI components
  • Selectively re-render entire component on change 

  • Can be rendered on client and server both
  • Two-way data binding
  • Browser support back to IE8

Components

Simple functions that take in Props and State to render HTML. 

Most SPA Frameworks

UI

Bindings

Observers

Templates

Motivation

Motivation

  • Model mutation
  • Mutation triggers observers and listeners
  • Can't use immutable data structures as your model
model.set('key', 'mad world');

Going Functional

f(state, props) = UI Fragment
  • Well-written components don't even need state
f(props) = UI Fragment

Idempotency

Immutability

Testability

Bliss

React has

Controllers

Directives

Templates

Global Event Listeners

Models

View Models

Just

Component

State Less

import React, { Component } from "react";

export class HelloWorld extends Component {
    render() {
        return ( <h1>Hello, world!</h1> );
    }
}


React.render(<HelloWorld />, document.getElementById("myDiv"));

JSX

  • A component implements a render method which returns one single child.

Stateful

import React, { Component } from 'react';

export class Timer extends Component {
  constructor() {
    super(props);

    this.state = {
      secondsElapsed: 0
    }
  }

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

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

  componentWillUnmount() {
    clearInterval(this.interval);
  }

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

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

Rerender

LifeCycle Methods

  • componentWillMount()
  • componentDidMount()

MOUNT

UNMOUNT

  • componentWillUnMount()

UPDATE

componentWillReceiveProps()

shouldComponentUpdate()

componentWillUpdate()

componentDidUpdate()

PROPS

  • passed from parent

STATES

  • created within component
<MyComponent foo="bar">
  • this.props read only
  • Can set default value
  • Validated → propTypes
// ES5
getInitialState: function() {
    return { foo: 'bar' }
}

// ES6 constructor
this.state = { foo: 'bar' }
  • Read with `this.state`
  • this.setState() to update.
  • Can become prop

Transferring props

import React, { Component } from 'react';

export class FancyCheckbox extends Component {
  render() {
    let fancyClass = this.props.checked ? 'FancyChecked' : 'FancyUnchecked';

    return (
      <div className={fancyClass} onClick={this.props.onClick}>
        {this.props.children}
      </div>
    );
  }
});

React.render(
  <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
    Hello world!
  </FancyCheckbox>,
  document.getElementById('example')
);

Props

Transferring props

  • No need to pass along all props 
  • Consume required and pass on remaining
  • Use JSX spread operator != ES6 spread operator

Examples

import React, { Component } extends 'react';

export class FancyCheckbox extends Component {
  render() {
    let { checked, ...other } = this.props;
    let fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';

    return (
      <div {...other} className={fancyClass} />
    );
  }
}

React.render(
  <FancyCheckbox checked={true} onClick={console.log.bind(console)}>
    Hello world!
  </FancyCheckbox>,
  document.getElementById('example')
);

DESTRUCTURING

CONSUMED

Props order matters

// code..

render() {
  var { checked, title, ...other } = this.props;
  var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
  var fancyTitle = checked ? 'X ' + title : 'O ' + title;
  
  return (
    <label>
      <input {...other}
        checked={checked}
        className={fancyClass}
        type="checkbox" // no matter what `other` is type will always be checkbox.
      />
      {fancyTitle}
    </label>
  );
}

// code..

Props Validation

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}

Counter.propTypes = { initialCount: React.PropTypes.number };

propTypes

  • array
  • bool
  • func
  • number
  • object
  • string
  • instanceOf
  • oneOf
  • oneOfType
  • arrayOf
  • any
  • node
  • element
  • shape

isRequired

* Use only in Development

States

  • Components are just state machines
  • Update the state and render new UI based on that state
  • Take data from props and render it
  • Components should be stateless as much as possible.
  • Create several stateless component and stateful component on top of that.

Should have

  • Minimal possible representation of data which triggers UI update

Should not have

  • computed data
  • Data that can be computed later
  • React Components
  • duplicated data from props

Multiple Components

  • Composability finest feature of react.
  • Using Components as (pure) functions or classes
  • separation of concerns
  • Owner Ownee relationship in React
  • Parent Child relationship in DOM

DEMO TIME

Separation of Components

  • composable
  • resuable
  • maintainable
  • testable

Non DOM attributes

  • key → keep component alive
  • ref → access dom node
  • dangerouslySetInnerHTML

Virtual DOM

Motivation

Modification in real DOM tree evil

  • it's incosistent
  • it's hard to test.
  • it's brittle
  • it's expensive

Virtual DOM

  • Consists of light weight Javascript Objects for DOM tree
  • Virtual DOM makes rerendering fast on every change
  • Batched read and write for optimal performence
  • When state changes Virtual and Real DOM trees are compared and real DOM tree modified only where it's required.
  • Virtual DOM != Shadow DOM

Example: In JSX

import React, { Component } from 'react';

export class Button extends Component {

    constructor(props) {
        super(props);

        this.state = { liked: false };
    }

    _handleClick() {
        this.setState({liked: !this.state.liked})
    }

    render() {
        let text = this.state.liked ? 'like' : 'unlike';
        
        return (
            <p onClick={() => this._onClick()}>
                You {text} this. Click to Toggle.
            </p>
        );
        
    }
}

Compiled to JS

import React, { Component } from 'react';

export class Button extends Component {

    constructor(props) {
        super(props);

        this.state = { liked: false };
        this. _handleClick = this._handleClick.bind(this);
    }

    _handleClick() {
        this.setState({liked: !this.state.liked})
    }

    render() {
        let text = this.state.liked ? 'like' : 'unlike';
        
        return (
            React.DOM.p({ onClick: this._handleClick },
                'You ', text, 'this. Click to Toggle'
            )
        );
        
    }
}

React.render

// REACT API
// returns a reference to the Component
// callback gets called after rendering.
ReactComponent render(ReactElement element, DOMElement container, [function callback]);
  • Render a ReactElement into the supplied container.
  • If ReactElement exists perform update and mutate
  • Does not modify the container node (only children)
  • It is possible to insert a component to existing DOM without overwriting the existing children

Problem

  • Transform one DOM tree to another 
  • well studied and solution is o(n3)
  • 1000 DOM elements ~ 1000000000 comparisons
  • Sure you want to do this?

React's O(n) Solution

Based on two assumptions

  • Two components
    with same class generates same tree
    with different classes generate different trees
  • Its is possible provide unique keys to stable component

Pairwise Diff

Pairwise Diff

  1. Different Node Types
  • if two nodes or components are of different types throw away first one and build/insert second one. 
renderA: <div />
renderB: <span />
=> [removeNode <div />], [insertNode <span />]
renderA: <Header /> // Component
renderB: <Content /> // component
=> [removeNode <Header />], [insertNode <Content />]

Pairwise Diff

2. Different DOM Nodes

  • Look at the attributes of both and can decide which of them changed in linear time
renderA: <div id="before" />
renderB: <div id="after" />
=> [replaceAttribute id "after"]
renderA: <div style={{color: 'red'}} />
renderB: <div style={{fontWeight: 'bold'}} />
=> [removeStyle color], [addStyle font-weight 'bold']
  • Treat style as key-value object instead - React

Pairwise Diff

2. Same Custom Components

  • Takes all attributes from new component
  • and calls component[Will/Did]ReceiveProps() on the previous one.
  • Components are stateful  so can't throw old component.

Listwise Diff

  • In case two ReactFragment go over both lists of children at the same time and generate mutation whenever there's difference.
// No mutation required.
renderA: <div><span>first</span></div>
renderB: <div><span>first</span><span>second</span></div>
=> [insertNode <span>second</span>]
// mutation required.
renderA: <div><span>first</span></div>
renderB: <div><span>second</span><span>first</span></div>
=> [replaceAttribute textContent 'second'], [insertNode <span>first</span>]
  • This is o(n2);

Listwise Diff

  • Add additional attribute `key`.
renderA: <div><span key="first">first</span></div>
renderB: <div><span key="second">second</span><span key="first">first</span></div>
=> [insertNode <span>second</span>]
  • key only has to be unique among its siblings, not globally.
  • React is now able to perform all tasks in O(n) with has table.
  • React is O(n). How?

Terminology

  • ReactElement
  • ReactNode
  • ReactComponent
  • ReactElement Factory
  • ReactComponent Class

ReactElement

  • Primary type in react
  • type, props, key and ref
  • No methods, nothing in prototype
var root = React.createElement('div');
React.render(root, document.getElementById('example'));
  • Light, stateless, immutable, virtual DOM

ReactElement

let root = <ul className="my-list">
             <li>Text Content</li>
           </ul>;
React.render(root, document.getElementById('example'));
var child = React.createElement('li', null, 'Text Content');

var root = React.createElement('ul', { className: 'my-list' }, child);

React.render(root, document.getElementById('example'));
  • JSX
  • JavaScript

properties

Children

ReactNodes

  • ReactElement
  • string ( aka ReactText )
  • number ( aka ReactText )
  • Array of reactNodes ( aka ReactFragment )

Used as properties of ReactElements to represent children

Can be either:

React Components

  • A simple JavaScript class
import React, { Component } from 'react';

export class MyComponent extends Component{
  render() {
    ...
  }
};
let component = new MyComponent(props); // never do this
let element = React.createElement(MyComponent); // use this
var element = <MyComponent />; // using JSX
  • Create ReactElement from ReactClass

Create Component

var component = React.render(element, document.getElementById('example'));
let componentA = React.render(<MyComponent />, document.getElementById('example'));
let componentB = React.render(<MyComponent />, document.getElementById('example'));

componentA === componentB; // true
  • Create ReactComponent from Element
  • ReactClass  ReactElement  ReactComponent
  • Same ReactElement and DOM Element → same reference

Factory

  • A function use to generate a ReactElement given a type
function createFactory(type) {
  return React.createElement.bind(null, type);
}
  • Allow you to create shorthand
let div = React.createFactory('div');
let root = div({ className: 'my-div' });
React.render(root, document.getElementById('example'));

Flux

ReactJS is a JavaScript library for building UI

Flux is application architecture for building UI

- Wikipedia

- Flux website

Traditional data flows

NO Framework

  • Any component can communicate with any other component

Backbone: Pub - Sub

model.on('change:name', function() { ... })

Angular: 2-way data binding and $digest loop

$scope.name = ...

React: 1-way data flow

Flux

  • Data flows in single direction 
  • Dispatcher: Hub to manage all data flow
  • Stores: Contains application state and logic 
  • Views: Representation of state within stores, listen to stores from depend stores
  • Actions: Dispatcher helper methods

Remember?

  • Props are immutable. 
  • States are mutable
  • Store states on topmost component
  • State can become Props.

Store, Actions And Views 

Reducer + Flux

By @gaearon

Redux

Motivation

Redux Principles

  • Single source of truth
  • State is read-only
  • Mutations are written as pure functions

The state of your whole application is stored in an object tree inside a single store.

The only way to mutate the state is to emit an action ( an object describing what happened )

To specify how the state tree is transformed by actions, write pure reducers.

Store Before

Store After

_recipes = [];

function addRecipes(recipe){
    _recipes.push(recipe);
}

let RecipeStore = Flux.createStore({
       getRecipes: function(){
       return _recipes;
    }
}, function(payload){
    if(payload.actionType === "ADD_RECIPE") {
        addRecipes(payload.text);
        RecipeStore.emitChange();
    }
});
function recipes(initialRecipes = [], action) {
    switch (action.type) {
        case 'ADD_RECIPE':
            return [..initialRecipes]
    }
}

import { combineReducers, createStore } from 'redux';
let reducer = combineReducers({ recipes });
let store = createStore(reducer);
// store.getState() will return app state

// actions
store.dispatch({
  type: 'ADD_RECIEPE',
  text: 'xyz'
});

// use connect() to receive a dispatch function
// as a prop,and any state it needs from the
// global state. 
Made with Slides.com