@kylefritz
http://slides.com/kylefritz/deck
Simple, fast, light
ideally :)
Professional Users
Powerful dashboards
Lots of Data
Real-Time Updates
Not "too much clicking"
class CartItem extends Marionette.ItemView
template: App.jst('cart_item')
modelEvents:
'change':'render'
class CartItems extends Marionette.CollectionView
itemView: CartItem
class Warnings extends Marionette.ItemView
template: App.jst('warnings')
class Cart extends Marionette.LayoutView
template: App.jst('cart')
regions:
warnings: '[data-region=warnings]'
items: '[data-region=items]'
onShow: =>
@warnings.show(new Warnings(model: @model.warnings()))
@items.show(new CartItems(collection: @model.cartItems()))
<h2>Cart</h2>
<div data-region="items"></div>
<div data-region="warnings"></div>
Usual pattern:
class Driver extends Marionette.ItemView
template: ...
modelEvents:
'change:name': 'render'
class Driver extends Marionette.ItemView
template: ...
modelEvents:
'change:name': 'render'
'change:availability': 'render'
'change:deliveryServiceId': 'render'
'change:location': 'renderLastUpdatedAtBadge'
class Driver extends Marionette.ItemView
template: ...
modelEvents:
'change:name': 'render'
'change:availability': 'renderAvailability'
'change:deliveryServiceId': 'renderDeliveryServiceId'
'change:location': 'renderLastUpdatedAtBadge'
renderAvailability: ->
# DOM manipulation
renderDeliveryServiceId: ->
# DOM manipulation
renderLastUpdatedAtBadge: ->
# DOM manipulation
class CartItem extends Marionette.ItemView
template: ...
modelEvents:
'change':'render'
Simply express how your app should look at any given point in time, and React will automatically manage all UI updates when your underlying data changes.
When the data changes, React conceptually hits the "refresh" button, and knows to only update the changed parts.
Your data
Models, collections, what have you
Most of your stuff
UI specific state
At first it seems like you should store lots of stuff in here but really, you want props :)
class CartItem extends Marionette.ItemView
template: App.jst('cart_item')
modelEvents:
'change':'render'
class CartItems extends Marionette.CollectionView
itemView: CartItem
CartItem = React.createClass
render: ->
<div>{@props.name}</div>
Cart = React.createClass
mixins: [Backbone.React.Component.mixin]
render: ->
<div>
{@props.collection.map (item)-> <CartItem {..item} />}
</div>
http://facebook.github.io/react/docs/reconciliation.html
PhotoGallery = React.createClass
render: ->
<div className="photo-gallery">
{@props.images.map (img) ->
<Photo {..img} />}
</div>
Photo = React.createClass
...
render: ->
...
<div className='photo'>
<span>{@props.caption}</span>
<br />
<button onClick={@toggleLiked} className={buttonClass}>
♥
</button>
<br />
<img src={@props.imageUrl} />
</div>
componentWillMount
invoked immediately before the initial rendering
componentDidMount
Invoked immediately after the initial rendering occurs.
Can use @getDOMNode() if you want to integrate with other JavaScript frameworks, set timers using setTimeout or setInterval, or send AJAX requests
componentWillUnmount
Perform any necessary cleanup before a component is unmounted
componentWillMount
invoked immediately before the initial rendering
componentDidMount
Invoked immediately after the initial rendering occurs.
Can use @getDOMNode() if you want to integrate with other JavaScript frameworks, set timers using setTimeout or setInterval, or send AJAX requests
componentWillUnmount
Perform any necessary cleanup before a component is unmounted
There is a hot-linkable jsx transformer (just for fun)
Better:
gem 'react-rails', '~> 1.0.0.pre',
github: 'reactjs/react-rails'
gem 'sprockets-coffee-react'
Create a something.jsx or something.js.cjsx file
#= require jquery
#= require react
#= require_tree .
HelloReact = React.createClass
render: ->
<div>
Hello React!!
</div>
$ ->
React.render(<HelloReact />, $('#hello-react')[0])
app/views/home/show.html.erb
<div id="hello-react"></div>
app/assets/javascripts/application.js.cjsx
@HelloReact = React.createClass
render: ->
<div>
Hello React!!
</div>
app/views/home/show.html.erb
<%= react_component('HelloReact') %>
app/assets/javascripts/components/hello.js.cjsx
#= require react
#= require react_ujs
#= require_tree .
app/assets/javascripts/application.coffee
@HelloReact = React.createClass
render: ->
<div>
Hello React!!
</div>
app/views/home/show.html.erb
<%= react_component('HelloReact', {}, {prerender: true}) %>
app/assets/javascripts/components/hello.js.cjsx
#= require react
#= require react_ujs
#= require_tree .
app/assets/javascripts/application.coffee
//= require_tree ./components
app/assets/javascripts/components.js
app/views/home/show.html.erb
<%= react_component('HelloReact', {important: @data}, {prerender: true}) %>
@HelloReact = React.createClass
render: ->
<div>
Hello {@props.important}!!
</div>
app/assets/javascripts/components/hello.js.cjsx
class Views.Messages extends Marionette.View
className: 'chat-pane-wrapper'
onShow: =>
componentElement = <MessagesList
collection={@collection}
driver={@model.toJSON()}
/>
React.render(componentElement, @el)
onDestroy: =>
React.unmountComponentAtNode(@el)
DriverPopover = React.createClass
mixins: [Backbone.React.Component.mixin]
...
handleDoneEditingDeliveryService: (deliveryServiceId)->
@getModel().save({deliveryServiceId})
@setState(editingDeliveryService: false)
render: ->
# if passed model: @props == model.attributes
# if passed collection: @props.collection == collection.toJSON()
case study:
cart = {
address: '2619 St Paul St'
items:[
{name: 'Pillows', quantity: 6}
{name: 'Sheets', quantity: 1}
{name: 'Blankets', quantity: 2}
{name: 'Side-Table', quantity: 2}
{name: 'Bed', quantity: 1}
]
}
Item = React.createClass
render: ->
<div>
<strong>{@props.name}</strong>
<span style={paddingLeft: '10px'}>x{@props.quantity}</span>
</div>
Cart = React.createClass
render: ->
<div>
<h4>Address</h4>
<address>{@props.address}</address>
<h4>Items</h4>
{@props.items.map((item)-> <Item {...item}/> )}
</div>
$ ->
React.render(<Cart {...cart} />, $('#hello-react')[0])
# api
@addItem = () ->
cart.items = cart.items.concat({name: 'Bed', quantity: 1})
React.render(<Cart {...cart} />, $('#hello-react')[0])
@changeName = (i, name) ->
cart.items[i].name = name
React.render(<Cart {...cart} />, $('#hello-react')[0])
case study:
Cart = React.createClass
...
render: ->
address = if @state.editing
<div>
<input ref="addressField" onChange={@updateAddress}
value={@props.address} />
<a onClick={@exitEditMode}>Done</a>
</div>
else
<div>
<address>{@props.address}</address>
<a onClick={@enterEditMode}>Edit</a>
</div>
<div>
<h4>Address</h4>
{address}
...
</div>
Cart = React.createClass
getInitialState: -> {editing: false}
enterEditMode: -> @setState(editing: true)
exitEditMode: -> @setState(editing: false)
updateAddress: ->
changeAddress(@refs.addressField.getDOMNode().value)
# <input ref="addressField" onChange={@updateAddress}
# value={@props.address} />
# <a onClick={@exitEditMode}>Done</a>
...
Cart = React.createClass
getInitialState: -> {editing: false}
enterEditMode: -> @setState(editing: true)
exitEditMode: -> @setState(editing: false)
updateAddress: ->
changeAddress(@refs.addressField.getDOMNode().value)
render: ->
address = if @state.editing
<div>
<input ref="addressField" onChange={@updateAddress} value={@props.address} />
<br/><a onClick={@exitEditMode}>Done</a>
</div>
else
<div>
<address>{@props.address}</address>
<br/><a onClick={@enterEditMode}>Edit</a>
</div>
<div>
<h4>Address</h4>
{address}
<h4>Items</h4>
{@props.items.map((item)-> <Item key={item.name} {...item}/> )}
</div>
@changeAddress = (address) ->
cart.address = address
React.render(<Cart {...cart} />, $('#hello-react')[0])
https://github.com/kylefritz/react-on-rails
(read the commit logs for all the reference links)