react-stuffs-slides



react-stuffs-slides

0 2


react-stuffs-slides

Presentation given 9/9/2015 at OC Tanner

On Github webbushka / react-stuffs-slides

React stuffs

This is the main slide... what could you possibly be expecting to see here?

AJ Webb

Software Engineer, Tanner Labs

@webbushka (everywhere)

I'm AJ Webb, I am a software engineer for Tanner Labs in salt lake city utah. I can be find all around the interwebs as @webbushka

slides

https://github.com/webbushka/react-stuffs-slides

Angular vs React

"React allows you to essentiallycreate a page, of single page apps"— Alexandr Marinenko

Angular Pros

  • Mostly transparent updates
  • Good Coupling
Mostly transparent updates Good Coupling -> you don't have to think about updating the data, it just happens

Angular Cons

  • Unpredictable performance
  • Can obscure data flow
  • Leaky watch abstraction
  • Directives and Services create scoping nightmares
    • Complects all the things
    • cannot ever be a page of SPAs

and WORST of all

Angular's style of dirty checking has the most unpredictable performance. obscures data flow aka MAGIC!* If you want an object to update another object you have to register watches whenever those are outside of the DOM

It's too much freaking...

React Pros

  • Explicit data flow
  • Reasonably predictable performance
  • Resuable components
    • Not coupled tightly to the application(so long as they aren't container components)
  • Components can fetch state or be hydrated with props
    • Each component is a small SPA
Very predictable data flow, the data always changes through the parent components. Then all the children re-render. React compares the DOM against itself to see if it needs to make a change. The idea that some owner in your view hierarchy that owns data and it gives permission downward to mutate it.

React Cons

  • Explicit updates
  • Performance edge cases
  • Server coordination can be difficult without flux
The parent to child data flow can be bad when you are getting data from a server then you have to figure out how to get that data to the correct parent components. Angular diffs all of the application's data to do its dirty checking and react just diffs what would appear on the screen to what is on the screen to see if it needs to update.

Welbe + React

=

Why is Welbe Moving to React?

  • Angular caused too many complexities
  • The Angular implementation had performance issues
  • Needed to use something more modular and performant

Flux

What is it?

"Flux is an application architecture for React"

Action creators are helper methods, collected into a library, that create an aciton from method parameters, assign it a type and provide it to the dispatcher. Every action is sent to all stores via the callbacks the stores register with the dispatcher. After stores update themselves in response to an action they emit a change event.Container (aka controller or parent) views, listen for change events, retrieve the new data from the stores and provide the new data to the entire tree of their child views.

It's Perfect!

Lessons Learned

(what would/will we do differently)

Reflux instead of Flux

WTF is reflux?

Immutable JS

Currently we either mutate data or do something like

let original = {a:'1', b:2, c:3};
let changed = JSON.parse(JSON.stringify(original));
changed.b = 50;
console.log(original.b) // 2
console.log(changed.b) // 50

when we don't want to mutate the data

Babel

Allows us to write ES6 and compiles it back to ES5

ES6

Let's see what's new!

Base Types in JavaScript

  • Number
  • String
  • Object
  • Undefined
  • Null
  • Boolean
  • Symbol

Variable Declaration

  • var
  • function
  • let
  • const
  • import
  • class

let & const

function scoped:

function() {
    if (true) {
        var i = 'hello';
    }
    console.log(i); // hello
}

block scoped:

function() {
    if (true) {
        const i = 'hello';
        let j = 'world';
    }
    console.log(i) // ReferenceError
    console.log(j) // ReferenceError
}

let & const

hoisted:

function() {
    console.log(a); // undefined
    var a = 'var';
}

not hoisted:

function() {
    console.log(a); // ReferenceError
    console.log(b); // ReferenceError

    let a = 'let';
    const b = 'const';
}

const must be immediately initialized and is "immutable"

const foo; // SyntaxError

While the value cannot change, the properties can

const foo = someObject;
foo = someOtherObject; // SyntaxError
foo.bar = 'hello';

Modules

  • Completely hoisted
  • Circular dependencies just work
  • Static analysis

Simple Syntax

import React from 'react';
import ApiClient from './lib/ApiClient';

class MyComponent extends React.Component {
    ...
}

export MyComponent;

Default Exports

// example.js
export default function() {...};

// example2.js
import ex from './example';

ex();

Named Exports

// example.js
export function ex1() {...};
export function ex2() {...};
export function ex3() {...};

// example2.js
import {ex2, ex3} from './example.js';

Import Aliasing

// example.js
export function ex1() {...};
export function ex2() {...};
export function ex3() {...};

// example2.js
import * as EX from './example.js';

EX.ex1();
EX.ex2();

Good

// component.js
import Header from './components/header';
import Button from './components/button';
import Footer from './components/footer';

render() {
    return (
        <Header />
        <Button />
        <Footer />
    )
}

Better

// index.js
import Header from './header';
import Button from './button';
import Footer from './footer';

export default { Header, Button, Footer };

// component.js
import { Header, Button, Footer } from './components/';

render() {
    return (
        <Header />
        <Button />
        <Footer />
    )
}

Best

export from

// index.js
export Header from './header';
export Button from './button';
export Footer from './footer';

// component.js
import { Header, Button, Footer } from './components/';

render() {
    return (
        <Header />
        <Button />
        <Footer />
    )
}

Class

Prototype inheritance

function Dog() {...}
Dog.prototype = new Animal();

Class inheritance

class Dog extends Animal {
    ...
}

Using with React

'use strict';
import React from 'react';

class EmployeeCount extends React.Component {
    constructor(props) {
        super(props);
        this.state = { company: props.company };
    }

    render() {
        return (
            <div>
                Active Employees: {this.state.company.employeeCount}
            </div>
        )
    }
}

EmployeeCount.propTypes = { company: React.PropTypes.object.isRequired };
EmployeeCount.defaultProps = { company: {} };

export default EmployeeCount;

Better

'use strict';
import React from 'react';

export class EmployeeCount extends React.Component {
    static propTypes = {
        company: React.PropTypes.object.isRequired
    }

    static defaultProps = {
        company: {}
    }

    constructor(props) {
        super(props);
        this.state = { company: props.company };
    }

    render() {
        return (
            <div>
                Active Employees: {this.state.company.employeeCount}
            </div>
        )
    }
}

Tacos!

'use strict';

const log = console.info.bind(console);

class MenuItem {
    constructor() {
        this.name = "Menu Item";
    }

    eat() {
        log(`Eating ${this.name}`);
    }
}

class Taco extends MenuItem {
    constructor() {
        super();
        this.name = 'Taco';
    }
}

class Burrito extends MenuItem {
    constructor() {
        super();
        this.name = 'Burrito';
    }
}

const items = [new Taco(), new Burrito()]

items.forEach(i => i.eat());

Compiled

'use strict';

var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var log = console.info.bind(console);

var MenuItem = (function () {
    function MenuItem() {
        _classCallCheck(this, MenuItem);

        log('Menu item created');
        this.name = 'Menu Item';
    }

    _createClass(MenuItem, [{
        key: 'eat',
        value: function eat() {
            log('Eating ' + this.name);
        }
    }]);

    return MenuItem;
})();

var Taco = (function (_MenuItem) {
    function Taco() {
        _classCallCheck(this, Taco);

        _get(Object.getPrototypeOf(Taco.prototype), 'constructor', this).call(this);
        log('Taco created');

        this.name = 'Taco';
    }

    _inherits(Taco, _MenuItem);

    return Taco;
})(MenuItem);

var Burrito = (function (_MenuItem2) {
    function Burrito() {
        _classCallCheck(this, Burrito);

        _get(Object.getPrototypeOf(Burrito.prototype), 'constructor', this).call(this);
        log('Burrito created');

        this.name = 'Burrito';
    }

    _inherits(Burrito, _MenuItem2);

    return Burrito;
})(MenuItem);

var items = [new Taco(), new Burrito()];

items.forEach(function (i) {
    return i.eat();
});

Babel is NOT perfect

Class inheritance has a small performance hit

the more you inherit the bigger the performance cut ... as with life, be smart with your inheritance

Too much inheritance

'use strict';

class MenuItem {
    constructor() {
        this.name = 'Menu Item';
    }

    eat() {
        console.info(`Eating ${this.name}`);
    }
}

class Taco extends MenuItem {
    constructor() {
        super();
        this.name = 'Taco';
    }
}

class HardTaco extends Taco {
    constructor() {
        super();
        this.name = 'Hard Taco';
    }
}

class BeefTaco extends HardTaco {
    constructor() {
        super();
        this.name = 'Beef Taco';
    }
}

class SpicyTaco extends BeefTaco {
    constructor() {
        super();
        this.name = 'Spicy Taco';
    }
}

class Burrito extends MenuItem {
    constructor() {
        super();
        this.name = 'Burrito';
    }
}

let items = [new Taco(), new Burrito(), new SpicyTaco()];

items.forEach(i => i.eat());

Destructuring

Before

const arr = [1, 2, 3];
const x = arr[0];
const y = arr[1];

function(obj) {
    const value = obj.value;
}

const name = this.props.name;
const dob = this.props.dobs;

After

const [x, y] = [1, 2, 3];

function({ value }) {}

const {name, dob} = this.props;

Avoid overly complex destructuring

// Fun
const {props: {fontStyle, user}, state: {name}, context: {router}} = this;
// More understandable
const {fontStyle, user} = this.props;
const {name} = this.state;
const {router} = this.context;

Before

updateState(payload) {
    const name = payload.name || 'stranger';
    this.setState({
        name: name
    });
}

After

updateState({ name = 'stranger'}) {
    this.setState({name});
}

Spread and Rest

// Arrays - ES6
const arr = [1, 2, 3];
...arr; // 1 2 3

// Objects - ESNext
const obj = {a: 1, b: 2, c: 3};
...obj; // {a: 1, b: 2, c: 3}

// JSX (supported by default)
<MyComponent {...this.props} />
const [x, y] = [1, 2, 3, 4];
x; // 1
y; // 2

const [x, y, ...rest] = [1, 2, 3, 4];
x; // 1
y; // 2
rest; // [3, 4]
const baseStyle = {
    background: '#b4d455',
    padding: 10
};

render() {
    const { altStyle } = this.props;
    const blendedStyle = { ...baseStyle, ...altStyle };

    return <div style={blendedStyle}>Yay!</div>;
}

Arrow functions

  • No arguments object
  • this is bound to lexical scope

The value of this inside of the function isdetermined by where the arrow function isdefined and not where it is used.— Understanding ECMAScript 6, Nicolas C. Zakas

Before

function(item) {
    return item.value + 1;
}

After

(item) => {
    return item.value + 1;
}

Better

({value}) => {
    return value + 1;
}

Best

({value}) => value + 1;

Decorators

Class level syntactic sugar for higher-order functions

Decorators are ES2016

  • The implementation is still in flux
  • Will be very similar to python decorators
  • They allow for code to run before and after modules (ex. auth logic)

Example of Decorators in React

'use strict';
import authorized from './decorator/authorized';
import clientWidth from './decorator/clientWidth';
import poll from './decorator/poll';

@authorized
@clientWidth
@poll
class SimpleView extends React.Component {
    ...
}

export default SimpleView;

Credits

Thanks to James White and Erin HoustonAttribution to Jem Young for his ES6 talk at React Rally

That's it

React stuffs This is the main slide... what could you possibly be expecting to see here?