Upload
geekslab
View
222
Download
9
Tags:
Embed Size (px)
Citation preview
ReactAlexey Volkov
Rumble Inc
http://slides.com/alexeyvolkov/react-in-practice
in practice
FacebookSince May 2013Current version 0.13.1Popular and it's growinghttps://github.com/facebook/react/wiki/Sites-Using-React
Everything is a componentEverything is a component
var HelloMessage = React.createClass({ render: function() { return <div>Hello {this.props.name}</div>; }});
React.render(<HelloMessage name="John" />, mountNode);
var Timer = React.createClass({ getInitialState: function() { return {secondsElapsed: 0}; }, tick: function() { this.setState({secondsElapsed: this.state.secondsElapsed + 1}); }, componentDidMount: function() { this.interval = setInterval(this.tick, 1000); }, componentWillUnmount: function() { clearInterval(this.interval); }, render: function() { return ( <div>Seconds Elapsed: {this.state.secondsElapsed}</div> ); }});
React.render(<Timer />, mountNode);
Just the UI ("V" in MVC)Virtual DOMSynthetic eventsIsomorphicOne-way data flowNOT A FRAMEWORK
Advantages
DeclarativePerformanceIsomorphicNice learning curveNice approach for separationof concernsNot a frameworkReact way = JavaScript waySomething else?
Disadvantages
Not a silver bulletNot a frameworkScatteringJSXYoungSomething else?
JSX hurts? Let's split it out
var Timer = React.createClass({ getInitialState: function() { return {secondsElapsed: 0}; }, tick: function() { this.setState({secondsElapsed: this.state.secondsElapsed + 1}); }, componentDidMount: function() { this.interval = setInterval(this.tick, 1000); }, componentWillUnmount: function() { clearInterval(this.interval); }, render: require('./Timer.render.js')});
React.render(React.createElement(Timer), mountNode);
Oyster
BBC
BigRen
tz
Beyondpad
BoomTown
Brigad
e
C5mail
CloudFlareCMNcom
Custom
Ink
Distiller
EMEX
EyeEm
FacebookFactlink
FaithStreet
Flexport
FiftyThreeMix
FINN FlightYogurt
Flipkart
GetStack
Guidebook
HackerOne
Html2CoffeeReact
ICX
Imgur
Instru
cture
Iodine
Itele
KISSmetrics
Kupibi
let Layer
LeFigaro LockedOn
AddThis
mPATH
Musixmatch
Netflix
NoRed
Ink
Orobix
RevUP PaddleGuruPatience
PivotalTracker
Pixate
AirBnBPodio
Posiq
Quizlet
QuizUp
Recurly
Reddit Redfin
Rollbar
Rushmore
Sauspiel
SberBank
AsanaSe
llerC
rowd
Sonian
Stampsy
Storehouse
Swipely
Tilt
Timerepublik
TMdic
t
TvTag
Uniregistry
Venmo
Verbling
Versal
Wagon
Wired
YahooZendesk
Zvooq
Taobao
Alipay
Aha
ENCODE Encyclopedia of DNA Ele
Glip Mobile
Khan Academy
Madrone Software Analytcs
Maxwell Health
Minerva Project
Palo Alto Software
Planning Center Online
Prism Skylabs
Rally Software
Rockefeller Center
Sift Science
Talk by Teambition
Traitify Developer Portal
Trunk
Club
University of Cincinnati
Vida Digital
https://github.com/facebook/react/wiki/Sites-Using-React
http://red-badger.com/blog/2015/03/04/react-native-the-killer-feature-that-nobody-talks-about/
"When I first looked at React I thought it wasinsane like most people. It takes such adifferent approach to web development thatit gives many people an immediate repulsivereaction. But of course the more I used it themore I realised I could never go back tobuilding web applications (or any front-endapp for that matter) any other way. Thepatterns react provides are an extremelypowerful way of building applications.
Backbone ModelsBackbone Models
CSSCSS
Mobile WebMobile Web
D3D3
Backbone Models? Why?Backbone Models? Why?SimpleWell-documentedLarge communityA long list of modules/plugins
React + BackboneReact + Backbonehttps://github.com/magalhas/backbone-react-componenthttps://github.com/clayallsopp/react.backbone
mixins: [mixinForm({ viewModelName: 'PushCampaign' })],...return <div> <c.Header title='< Push Campaign Editor' level={2} innerRight={buttons} /> <c.Fieldset caption='Message Composer'>
<c.FieldRow caption='Campaign Name:' className={this.form().labelClassName('Name')} hint={this.form().errors('Name')}> <c.Input type='text' {...this.form().attribute('Name')} /> </c.FieldRow>
<c.FieldRow caption='Description:' subCaption='For internal use only'> <c.Input type='textarea' {...this.form().attribute('Notes')} /> </c.FieldRow>
</c.Fieldset> <c.Fieldset caption='Message Targeting'>
<c.FieldRow caption='Send Notification To:' className={this.form().labelClassName('_targeting')} hint={this.form().errors('_targeting')}> <c.RadioList items={targetingItems} {...this.form().attribute('_targeting')} /> </c.FieldRow>
</c.Fieldset> <c.Header level={2} innerRight={buttons} /></div>;
return <div> <c.Header title='< Push Campaign Editor' level={2} innerRight={buttons} /> <c.Fieldset caption='Message Composer'>
<c.FieldRow caption='Campaign Name:' className={this.form().labelClassName('Name')} hint={this.form().errors('Name')}> <c.Input type='text' value={this.form().value('Name')} className={this.form().inputClassName('Name')} onChange={this.form().handleChange('Name')} /> </c.FieldRow>
<c.FieldRow caption='Description:' subCaption='For internal use only'> <c.Input type='textarea' {...this.form().attribute('Notes')} /> </c.FieldRow>
</c.Fieldset> <c.Fieldset caption='Message Targeting'>
<c.FieldRow caption='Send Notification To:' className={this.form().labelClassName('_targeting')} hint={this.form().errors('_targeting')}> <c.RadioList items={targetingItems} {...this.form().attribute('_targeting')} /> </c.FieldRow>
</c.Fieldset> <c.Header level={2} innerRight={buttons} /></div>;
componentWillMount: function () {
var model = this.form().model;
if (model.get('RegistrationId')) { model.set('_targeting', 'id'); }
// hide error message for RegistrationId (if user switched targeting type to "All") model.on('change:_targeting', function (model) { if (model.get('_targeting') === 'all') { model.set('RegistrationId', ''); this.form().validate({RegistrationId: model.get('RegistrationId')}); } }, this);
// clear link validation status on link editing model.on('change:ArticleUrl', function (model) { model.set('_articleUrlValid', null); if (_.isEmpty(model.get('ArticleUrl'))) { this.setState({notificationLink: LINK_EMPTY}); } else { this.forceUpdate(); } }, this); },
componentWillUnmount: function () { this.form().model.off(null, null, this); },
var PushCampaign = Backbone.Model.extend({
defaults: { 'Name': '', 'Message': '', '_targeting': 'all' },
validation: { Name: { required: true }, Message: { required: true, maxLength: 250 },
RegistrationId: function (value) { if (this.get('_targeting') === 'id' && _.isEmpty(value)) { return 'Registration ID is missed'; } } }
});
<FillPicker {...this.form().attribute('brandColors[name=main]')} />
<ImageUploader value={this.form().value('icons[name=ipad152x152].uri')} onChange={this.form().onChange('icons[name=ipad152x152].uri')} hint="152x152" deletable={true}/>
module.exports = Backbone.AssociatedModel.extend({ ... relations: [ { key: 'icons', type: Backbone.Many, relatedModel: 'Image', collectionType: 'ImagesCollection' }, { key: 'brandColors', type: Backbone.Many, relatedModel: Backbone.AssociatedModel.getRelatedModel('Fill'), collectionType: 'FillsCollection' } ] ...});
npm installnpm installreact-validator-mixinreact-validator-mixin
https://github.com/RumbleInc/https://github.com/RumbleInc/react-validator-mixinreact-validator-mixin
React + BackboneReact + BackboneForm data validationModels in flux storesRouter<Your ideas>?
CSS. What is the problem?CSS. What is the problem?
One global namespaceDependenciesSharing constants
https://speakerdeck.com/vjeux/react-css-in-js
CSS. Solutions?CSS. Solutions?"It's not a bug. It's a feature!"LESS/SASS/...BEM (block-element-modificator)
inline styles-anything: else?React-Styler
https://github.com/albburtsev/bem-cn
'use strict';
var React = require('react/lib/ReactWithAddons'), styler = require('react-styler');
var Fieldset = React.createClass({
mixins: [styler.mixinFor('Fieldset')],
render: function () { var cn = this.className; /* jshint ignore:start */ return
/* jshint ignore:end */ }});
module.exports = Fieldset;
<fieldset className={cn()} style={this.props.style}> {this.props.caption && <legend className={cn('caption')}>{this.props.caption}</legend>} {this.props.children} </fieldset>;
React.render(<Fieldset />, mountNode);
<fieldset class="Fieldset"> <legend class="Fieldset-caption">Caption</legend> ...</fieldset>
React.render(<Fieldset className="group1" />, mountNode);
<fieldset class="Fieldset Fieldset-group1"> <legend class="Fieldset-caption">Caption</legend> ...</fieldset>
styler.registerComponentStyles('Fieldset', { border: '1px solid #dbdbdb', padding: '35px 20px 20px',
'& + &': { marginTop: 25 },
'&-caption': { color: '#474747', padding: '0 8px', marginLeft: -8 }
});
styler.registerComponentStyles('ChartPushNotifications', { '& .LineChart-lines path:nth-of-type(2)': { transform: 'translateY(-2px)' }, '& .LineChart-background': { fill: '#f5f5f5' }, '&-tooltip': { padding: 10, textAlign: 'center' }, '&-tooltip-channel': { fontSize: 12, maxWidth: 180 }, '&-tooltip-channel:before': { content: '"\\2588"', display: 'inline-block', width: 10, height: 10, overflow: 'hidden', verticalAlign: 'middle', marginRight: 5, fontSize: '1em', lineHeight: '10px' }});
.ChartPushNotifications {}.ChartPushNotifications .LineChart-lines path:nth-of-type(2) { transform: translateY(-2px);}.ChartPushNotifications .LineChart-background { fill: #f5f5f5;}.ChartPushNotifications-tooltip { padding: 10px; text-align: center;}.ChartPushNotifications-tooltip-channel { font-size: 12px; max-width: 180px;}.ChartPushNotifications-tooltip-channel:before { content: "\2588"; display: inline-block; width: 10px; height: 10px; overflow: hidden; vertical-align: middle; margin-right: 5px; font-size: 1em; line-height: 10px;}
Styler. Advantages?Styler. Advantages?BEM-like syntaxDependenciesNamespacesConstants/variablesVery easy to learn and use
npm install react-stylernpm install react-styler
https://github.com/RumbleInc/https://github.com/RumbleInc/react-stylerreact-styler
React + Mobile WebReact + Mobile Web
React + Mobile WebReact + Mobile WebMinimize DOM modifications(use React, avoid jQuery)Optimise Virtual DOM(use shouldComponentUpdate)Be smart with CSS
http://goo.gl/8Ybdgnhttp://goo.gl/8Ybdgn
React + Mobile WebReact + Mobile Webreact-canvas (from Flipboard)react-native (congrats! just went public)Not WEB, it's native!
React + D3React + D3
React + D3. ApproachesReact + D3. Approaches1. React as a wrapper2. React as a renderer
render: function() { return <div> <div ref="chart"></div> </div>;},
componentDidMount: function() { var chartDOMNode = this.refs['chart'].getDOMNode();
var m = 7; // number of samples per layer
var margin = {top: 4, right: 0, bottom: 10, left: 0}, that = this;
this.width = chartDOMNode.clientWidth - margin.left - margin.right; this.height = chartDOMNode.clientHeight - margin.top - margin.bottom;
var x = d3.scale.ordinal() .domain(d3.range(m)) .rangeRoundBands([39, this.width], 0.85, 0);
this.y = d3.scale.linear() .domain([0, 0]) .range([this.height, 0]);
this.yAxis = d3.svg.axis() .scale(this.y) .orient('left') .tickSize(-this.width, 0)
render: function () { var cn = this.className;
/* jshint ignore:start */ var paths = prepareData(this.state.series);
return <div onClick={this.handleClickUpdate}> <svg width={width} height={height}> <g> {_.map(paths, function (paths, index) { return <g key={index}> {paths.series && <path {...paths.series} />} <g style={paths.series.style}> {_.map(paths.points, function (point, pointIndex) { return <path key={pointIndex} {...point} />; })} </g> </g>; }, this)} </g> </svg> </div>; /* jshint ignore:end */}
CommonCommonD3 - calculationsD3 - renderingReact - stupidwrapper
WiseWiseD3 - calculationsReact - rendering
??