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
- 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.
deck
By Jignesh Kakadiya
deck
- 2,395