React Not Just Hype!
@mjackson Thriller.
@ReactJSTraining reactjs-training.com
rackt github.com/rackt
- 14 members - 5 teams (owners, routing, redux, a11y, docs) - Not exclusive! This is not a core team.
Hype! React Conf, January 2015 - At React Conf in January, Ryan gave talk called Hype! - Many developers of competing frameworks had publicly intimated that React was just the latest manifestation of the Hype Cycle
- Unfortunately the demo that received the most attention was dbmon
WTF!! see how fast is react s rendering engine! react.js vs angular vs ember! Top YouTube commenter - People thought it was a speed comparison - So of course everyone went out and implemented that demo in their own framework, ignoring the other examples
React lets you say YES - Can you sort and magically animate elements to their new positions without manually aligning everything to your own grid system? Yes. - Can you build an itunes-style UI that dynamically allocates child view positions depending on the route you re on? Yes. - Can you render stuff on the server? Yes.
Why? - What is it about React that let s you say yes? - Is it JSX, the tooling, the community, server rendering, the component model? - I m going to assume familiarity with Angular, Ember, Backbone, and React
It s amazing what you can do with the right abstraction. Tom Occhino - This is a low-level talk - What makes a good abstraction? - Depends on environment: stuff that works well in one environment may not apply in another
Problem: Building UI - Templates were a necessary abstraction for building HTML on the server - Not necessary on the client; you re already in the view!
<div ng-repeat="model in collection"> {{model.name}} </div> - Where does collection come from? - ng-init directive? $scope? $rootscope? - I have to understand what ng-repeat does in order to know there may be more than a single <div>
<div ng-repeat="model in collection"> {{model.name}} </div> collection.map(model => ( React.createElement('div', null, model.name) )) - collection is just a variable in scope; no need to invent our own concept of scope - no need for model in DSL, use Array#map
<div ng-repeat="model in collection"> {{model.name}} </div> collection.map(model => ( React.createElement('div', null, model.name) )) collection.map(model => ( <div>{model.name}</div> )) - use JSX to eliminate createelement cruft -
<div ng-repeat="model in collection"> {{model.name}} </div> collection.map(model => ( <div>{model.name}</div> )) - This is what we re really comparing - Just use JSX
<div ng-repeat="model in collection track by model.id"> {{model.name}} </div>
<div ng-repeat="model in collection track by model.id"> {{model.name}} </div> collection.map(model => ( <div key={model.id}>{model.name}</div> ))
<div ng-repeat="model in collection track by $index"> {{model.name}} </div> - What if we want to track by array index? - Magic $index var!
<div ng-repeat="model in collection track by $index"> {{model.name}} </div> collection.map((model, index) => ( <div key={index}>{model.name}</div> )) - If we just use JavaScript, we can use map s index! - No need to learn special DSL or magic vars!
- Let s say we want to create this HTML <select name="month"> <option>(01) January</option> <option>(02) February</option>... </select>
<select name="month"> <option>(01) January</option> <option>(02) February</option>... </select> <select name="month"> <option ng-repeat="month in months"> ({{$index padmonth}}) {{month}} </option> </select> - Stuff I need to know: - month in months DSL - magic $index var - is called a filter
<select name="month"> <option>(01) January</option> <option>(02) February</option>... </select> <select name="month"> <option ng-repeat="month in months"> ({{$index padmonth}}) {{month}} </option> </select> <select> {months.map((month, index) => ( <option>({padmonth(index)}) {month}</option> ))} </select> - If we just use JavaScript, we can use map s index! - No need to learn special DSL or magic vars!
JavaScript Templates Use vars in function scope Guess where vars come from Use JavaScript methods like Array#map Invent our own DSL Carry knowledge to next project Describe ourselves as X developers - Every template language eventually incorporates all the features of JavaScript so just use JS! - JavaScript is a better abstraction than templates for building HTML/UI
- When building UI is completely decoupled from rendering it, we can build custom renderers - React.renderToString
- react-art is React bindings for ART - ART is vector drawing API with multiple output modes (canvas, VML, SVG, DOM)
- Anyone can build a renderer for React; not just Facebook! - Flipboard built a <canvas> renderer
- react-three controls three.js scenes using React
- react-blessed renders to the terminal using the blessed (terminal interface for node.js)
- Netflix uses Gibbon C++ lib to render React to TVs - When building UI is a generic process, we are totally decoupled from the DOM
Problem: Managing State - When does state change in your app? - How do you communicate that change to the rest of the system?
Person = Ember.Object.extend({ // these will be supplied by `create` firstname: null, lastname: null, fullname: function() { var firstname = this.get('firstname'); var lastname = this.get('lastname'); return firstname + ' ' + lastname; }.property('firstname', 'lastname'), fullnamechanged: function() { // deal with the change }.observes('fullname').on('init') }); var person = Person.create({ firstname: 'Michael', lastname: 'Jackson' }); person.set('firstname', 'Joe'); - State is kept in many places - Many side-effects to using set() - Easy to introduce bugs
- This will log the old value of fullname because observers are sync Person.reopen({ lastnamechanged: function() { // This logs the previous value of fullname! console.log(this.get('fullname')); }.observes('lastname') });
Observers in Ember are synchronous. This means that they will fire as soon as one of the properties they observe changes. Because of this, it is easy to introduce bugs where properties are not yet synchronized. Ember Observers Guide
Person.reopen({ partofnamechanged: function() { // This function will fire twice! }.observes('firstname', 'lastname') }); person.set('firstname', 'Michael'); person.set('lastname', 'Jackson'); - Manually observing changes to state can lead to subtle bugs - How to fix this? Use Ember.run.once!
Person.reopen({ partofnamechanged: function() { Ember.run.once(this, 'processfullname'); }.observes('firstname', 'lastname'), processfullname: function() { // This will only fire once! console.log(this.get('fullname')); } }); person.set('firstname', 'John'); person.set('lastname', 'Smith'); - Ember.run.once manually schedules a function on the run loop - Now we're thinking about timing!
- This approach to managing changes to state is called KVO - KVO is an old programming paradigm from Mac OS X - Angular $scope dirty checking is same paradigm KVO
Our intellectual powers are rather geared to master static relations and that our powers to visualize processes evolving in time are relatively poorly developed. For that reason we should do (as wise programmers aware of our limitations) our utmost to shorten the conceptual gap between the static program and the dynamic process, to make the correspondence between the program (spread out in text space) and the process (spread out in time) as trivial as possible. Edsger W. Dijkstra, A Case Against the GOTO Statement - Translation: We need to remove time from the equation in order to predict how our code will behave - We shouldn't have to think about time
- Apple has totally moved away from KVO in ios SDK KVO is HARD
KVO is HARD Object.observe - We re re-inventing KVO in JS with Object.observe :'( - Let's not go down that route! - Is there a better abstraction for managing state updates?
1. What state is there? 2. When does it change? - There are 2 important questions in the problem of managing state
const Person = React.createClass({ getinitialstate() { return { username: '' } }, sayhelloto(username) { this.setstate({ username: username }) }, render() { let username = this.state.username return ( <p>hi{username? ', ' + username : ''}!</p> ) } }) - All component state is encapsulated in this.state - It changes whenever you call setstate
class Person extends React.Component { constructor(props, context) { super(props, context) this.state = { username: '' } } sayhelloto(username) { this.setstate({ username }) } } render() { let { username } = this.state return ( <p>hi{username? ', ' + username : ''}!</p> ) } - If you're using ES6 classes, setup this.state in your constructor
setstate KVO One object contains state Many objects contain state Imperative, intent is obvious Behavior depends on side effects Easy to grep Difficult to grep - setstate is a better abstraction for updating state than KVO -
Problem: Updating UI
View Logic DOM - View logic is typically called controller/model - When you render, it s your job to go update the DOM
View Logic Virtual DOM DOM -
Virtual DOM DOM Describe what UI looks like Actually manipulate DOM Framework handles optimization Optimize by hand Easy to introduce new render targets Difficult to render to new targets
Problem: Feature Parity - Feature parity is a real problem at every tech company on more than one platform (e.g. Twitter) - Isn't React just a JavaScript framework? Isn't this an organizational issue?
ios Android Web
Chat Ads News
Chat Ads News
React Native - Learn once, write anywhere -
We refactor[ed] platform-specific parts of the UI into separate components that would have an Android and ios implementation. At the time of shipping Ads Manager for Android, that approach yielded around 85 percent reuse of app code. Facebook
Thanks! @mjackson