Eirik Langholm Vullum PRO
JavaScript fanatic that loves node.js and React.
A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES
Application
DOM
mutate state
mutate state
read state
read state
:(
React
(virtual DOM)
(JavaScript)
DOM
(browser)
Application
state
patch DOM
patch DOM
props
change
but what about perf..?
var HelloWorldComponent = React.createClass({
render: function() {
return (
<div className="some-style">
<p>Hello World</p>
</div>
);
}
});
It's all JavaScript
var HelloWorldComponent = React.createClass({
render: function() {
return (
React.createElement("div", { className: "some-style" },
React.createElement("p", null, "Hello World");
);
);
}
});
ReactDOM.render(
<HelloWorldComponent/>,
document.querySelector('#mountNode');
);
But what about state, changes and everything that makes my app dynamic?
var HelloWorldComponent = React.createClass({
render: function() {
return (
<div className="some-style">
<p>{this.props.greeting}</p>
</div>
);
}
});
<HelloWorldComponent greeting='Hello World' />
<HelloWorldComponent greeting={'Hello ' + 'World'} />
<HelloWorldComponent greeting={getGreeting()} />
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>
);
}
});
var HelloWorldComponent = React.createClass({
getInitialState: function() {
return {
count: 0
};
},
handleClick: function () {
this.setState({
count: this.state.count + 1
});
},
render: ...
});
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>
);
}
});
var HelloWorldComponent = React.createClass({
getInitialState: ...
handleClick: function (event) {
event.preventDefault();
this.setState({
count: this.state.count + 1
});
},
render: ...
});
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>;
}
});
First render call
Subsequent
render calls
Updating state
getDefaultProps
getInitialState
componentWillMount
render
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
Being removed from the DOM
componentWillUnmount
var MyComponent = React.createClass({
// lifecycle methods
getDefaultProps: ...
getInitialState: ...
componentWillMount: ...
componentDidMount: ...
componentDidUpdate: ...
componentWillReceiveProps: ...
componentWillUnmount: ...
shouldComponentUpdate: ...
// custom methods
handleThis: ...
handleThat: ...
computeThis: ...
computeThat: ...
// rendering
render: ...
});
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>
}
});
var SetIntervalMixin = {
componentDidMount: function() {
this._intervals = [];
},
componentWillUnmount: function() {
this._intervals.forEach(function(interval) {
clearInterval(interval);
});
},
setInterval: function() {
this._intervals.push(setInterval.apply(null, arguments));
}
});
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>
}
});
<ChatApp />
<FriendList />
<MessageList />
<Friend />
<Friend />
<Friend />
<Friend />
<Message />
<Message />
<Message />
<MessageInput />
var ChatApp = React.createClass({
...
render: function() {
return (
<div>
<FriendList friends={this.state.friends} />
<MessageList messages={this.state.messages} />
<MessageInput handleInput={this.handleInput} />
</div>
);
}
});
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>;
}
});
var Message = React.createClass({
render: function() {
return (
<li>
<span>From: {this.props.from}</span>
<span>Content: {this.props.content}</span>
</li>
);
}
});
But where should i put all my state and stuff..?
<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
So won't this turn into a mess when my app grows in complexity..?
backbone-react-component
react-backbone
// 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!');
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]);
});
}
Interacting with the DOM through
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>;
}
});
(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"))})
<Surface>
<Layer>
<Group>
<Text>
<Image>
<ListView>
<div>
<span>
<p>
<ul>
<li>
<table>
vs.
<Image>
<ListView>
<MapView>
<Navigator>
<ScrollView>
<Text>
<TextInput>
<View>
<WebView>
...
<div>
<span>
<p>
<ul>
<li>
<table>
vs.
Pretty much anything!
React Workshop - May 6-7.
Building Isomorphic Apps in JavaScript
Talk - June 19.
By Eirik Langholm Vullum
Talk for NDC Meetup