Food
Transport

@kylefritz

http://bit.do/ou-delivery

HI, I'm Kyle

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

  • Crowd-sourced, restaurant food-delivery platform
     
  • We use our own driver pool to deliver good and interesting food to our customers
     
  • 30 cities +  1,000 drivers
    (not in the top 10 metros)

Consumer Apps

Simple, fast, light

ideally :)

Operations Apps

Professional Users

Powerful dashboards

Logistics & Optimization

Lots of Data

Real-Time Updates

Not "too much clicking"

We have a lot of internal tools

Dispatcher UI

render

@PROPS
@state

JSX is simpler to understand
AND more powerful

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>

w/ JSX

w/o JSX

JSX is just Syntax

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))
        )
      )
    );
  }
});

AND react is Still Very much
Component Based and Composible

And wow is it performant!

And wow is it performant!

LIFECYCLE METHODS

  • getInitialState
  • 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

UPDATING METHODS

  • componentWillReceiveProps
    react to updated props before render (animations)
  • shouldComponentUpdate
    block rendering
  • componentWillUpdate
  • componentDidUpdate

 

component spec is really small

REACT
on
RAILS

Strategies for organization/isolation of components

Transform JSX

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

Hello React

#= 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

React w/ View Helpers and react_UJS

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

Some kind of module System?

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

Some kind of module System?

suggestion: modulejs

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. 

AMD-Style Loading

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 .

Super-Cool Logging

Zero other Features =>

Asset Pipeline loves it

Kicking off the Processs

#= 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

Organizational Tips

  • 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

data

between

React + Rails

React ujs:
Pass into second View Helpers arg to encode as a data-

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

Option: Use Gon

Your Rails variables in your JS

https://github.com/gazay/gon

Option: Use 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

But Which Attributes go in?

Consider Active Model Serializers

# 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

AMS plays well w/ Data Exchange 

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

Fetch

  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)
      )

Alternative techniques

React w/ Server REndering

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

Pushing Data to React

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

More sophisticated Asset Packaging

Firebase

@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

React w/ Rails

  • Simple to learn (like rendering views and partials)
     
  • Easy to plug into your existing app in a small or big way
     
  • Performant enough to lean-in on

http://bit.do/nation-react

https://github.com/kylefritz/react-on-rails

(read the commit logs for all the reference links)

Thanks!

@kylefritz

Appendix

REACT
    +
Marionette

A marionette Adapter View

  class Views.Messages extends Marionette.View
    className: 'chat-pane-wrapper'

    onShow: =>
      componentElement = <MessagesList
        collection={@collection} />
      React.render(componentElement, @el)

    onDestroy: =>
      React.unmountComponentAtNode(@el)

Backbone.React.Component.mixin

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

Copy of React on Rails: modular Javascript that is the right for your app

By Kyle Fritz

Copy of React on Rails: modular Javascript that is the right for your app

Ruby Nation June 2015

  • 1,516