Kyle Fritz
baltimore technologist. gravity skeptic. director of reverse engineering at @OrderUp.
@kylefritz
http://bit.do/ou-delivery
I'm into bikes, gardening & being outside
I teach full-stack web development at Betamore in Rails or Node
I love seeing the internet jump out of computers and affect the real world
Simple, fast, light
ideally :)
Professional Users
Powerful dashboards
Logistics & Optimization
Lots of Data
Real-Time Updates
Not "too much clicking"
class PhotoGallery extends React.component
render: ->
<div className="photo-gallery">
{@props.images.map (img) ->
<Photo {..img} />}
</div>
class Photo extends React.component
...
render: ->
...
<div className='photo'>
<span>{@props.caption}</span>
<br />
<button onClick={@toggleLiked} className={buttonClass}>
♥
</button>
<br />
<img src={@props.imageUrl} />
</div>
var TodoApp = React.createClass({
...
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
var TodoApp = React.createClass({
...
render: function() {
return (
React.createElement("div", null,
React.createElement("h3", null, "TODO"),
React.createElement(TodoList, {items: this.state.items}),
React.createElement("form", {onSubmit: this.handleSubmit},
React.createElement("input", {onChange: this.onChange, value: this.state.text}),
React.createElement("button", null, 'Add #' + (this.state.items.length + 1))
)
)
);
}
});
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 react
class HelloReact extends React.component
render: ->
<div>
Hello React!!
</div>
document.addEventListener "DOMContentLoaded", ->
React.render <HelloReact />, document.getElementById('hello-react')
app/views/home/show.html.erb
<div id="hello-react"></div>
app/assets/javascripts/application.js.cjsx
app/views/home/show.html.erb
<%= react_component('HelloReact') %>
<!-- becomes: -->
<div data-react-class="HelloReact"></div>
#= require react
#= require react_ujs
class HelloReact extends React.component
render: ->
<div>
Hello React!!
</div>
app/assets/javascripts/application.js.cjsx
app/assets/javascripts/orders/driver.js.cjsx
class Driver extends React.component
render: ->
# show a tiny summary
app/assets/javascripts/drivers/driver.js.cjsx
class Driver extends React.component
render: ->
# show the whole profile for a Driver
modulejs is a lightweight JavaScript module system. It is not a module loader, it triggers no file system lookups or HTTP requests. It simply helps organizing code in small, maintainable and easy to use modules. Modules respect and resolve dependencies, the syntax is very similar to that of RequireJS.
app/assets/javascripts/orders/driver.js.cjsx
modulejs.define 'orders/driver', ->
class Driver extends React.component
render: ->
# show a tiny summary
app/assets/javascripts/drivers/driver.js.cjsx
modulejs.define 'drivers/driver', ['orders/driver'], (DriverBadge) ->
class Driver extends React.component
render: ->
# show the whole profile for a Driver
# can use <DriverBadge /> since imported
app/assets/javascripts/application.coffee
#= require modulejs
#= require_tree .
#= require modulejs
#= require_tree .
modulejs.define 'entry_point', ['app'], (App) ->
return (el) ->
React.render <App />, el
#delivery-chat
coffee:
startApp = modulejs.require('entry_point')
startApp(document.getElementById('delivery-chat'))
app/assets/javascripts/some_app/index.js.cjsx
app/views/some_app/show.slim
One React component per file makes them easier to find plus room for growth
ABC: Always be composing
Keep child components in a folder w/ their "orchestrating" component
app/views/home/show.html.erb
<%= react_component('HelloMessage', name: 'John') %>
<!-- becomes: -->
<div data-react-class="HelloMessage"
data-react-props="<...quoted json for {name: 'John'}>"></div>
#= require react
#= require react_ujs
class HelloReact extends React.component
render: ->
<div>
Hello {@props.name}!!
</div>
app/assets/javascripts/application.js.cjsx
Your Rails variables in your JS
https://github.com/gazay/gon
app/controller/delivery_controller.rb
class DeliveryController < ApplicationController
def show
gon.push(driver: Driver.last)
end
end
React.render <Driver {...gon.driver} />, document.getElementById('delivery-widget')
app/assets/javascripts/application.js.cjsx
# ALPHABETIZE attributes & methods
class Dispatcher::DriverSerializer < Dispatcher::BaseSerializer
attributes \
:available,
:banned,
:phone,
:platform
def banned
object.restaurant_bans.map(&:restaurant_id)
end
def phone
number_to_phone(object.phone) if object.phone.present?
end
end
class SomeController < ApplicationController
def show
driver = Driver.find(params[:id])
gon.push(driver: DriverSerializer.new(driver).serializable_object)
end
end
class Driver
after_save :pusher
private
def pusher
Pusher.trigger('private-channel', 'driver',
DriverSerializer.new(driver).serializable_object)
end
end
class JsonOtherController < ApplicationController
def show
driver = Driver.find(params[:id])
render json: driver, serializer: DriverSerializer
end
end
class Driver extends React.Component
state:
loading: true
componentDidMount: =>
fetch("/drivers/#{@props.driverId}", {credentials: 'include'}
).then((r)-> r.json()
).then((driver) =>
@setState(driver)
@setState(loading: false)
)
class @HelloReact extends React.component
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
@TodoApp = React.createClass
# mixins: [ReactFireMixin]
componentWillMount: =>
@firebaseRef = new Firebase("https://ReactFireTodoApp.firebaseio.com/items/");
@firebaseRef.on("child_added", (dataSnapshot) =>
@items.push(dataSnapshot.val());
this.setState(items: @items)
render: =>
# use @state.items in a meaningful way
https://github.com/kylefritz/react-on-rails
(read the commit logs for all the reference links)
class Views.Messages extends Marionette.View
className: 'chat-pane-wrapper'
onShow: =>
componentElement = <MessagesList
collection={@collection} />
React.render(componentElement, @el)
onDestroy: =>
React.unmountComponentAtNode(@el)
DriverPopover = React.createClass
# "collection" & "model" are treated as special props by this mixin
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()
# would normally use
class DriverPopover extends React.component
# but mixins not supported for class definitions
By Kyle Fritz
Ruby Nation June 2015
baltimore technologist. gravity skeptic. director of reverse engineering at @OrderUp.