React-Rails with Flux (Alt.js) Tutorial

By Blaine Hatab | October 31, 2015

Why I’m using Flux and React in Rails

I've been integrating react into my rails apps for a little while now. You can see the 3 main ways I've been integrating react with rails here, http://www.openmindedinnovations.com/blogs/3-ways-to-integrate-ruby-on-rails-react-flux. The simplest way for a rails dev to get up and running with react is to use the react rails gem. A lot of developers having success with that, but as your app scales you'll find a lot of problems when not using a front end data managment layer like flux or some equivilent. There are lot of other good systems out there including my current favorite which is using webpack/redux/react (https://github.com/shakacode/react_on_rails), but I'm going to focus on integrating flux into rails using the react rails gem. The key to this setup is that I don't use npm at all and I just use sprockets, which makes the process much smoother for a rails developer.

I'll be using the following libraries in this template:

Alt (http://alt.js.org/)
React Rails (https://github.com/reactjs/react-rails)
Lodash (https://lodash.com/)

Some of these are preference, but this tutorial will walk you through the core setup so you can use whatever you want.

Setup React Rails Gem

First you will need to add the react rails gem to your rails project. Go to their github page for more details on setup, https://github.com/reactjs/react-rails. All you need to do is add it to your gem file, bundle install, and run their generator.

rails g react:install

You'll end up with a components.js file in your javascripts that loads all the react code. This file is special because react rails loads this file using exec js if you use their server rendering feature. This is important because if you use flux, you'll need to makes sure all of your flux assets are loaded into this component.js file as well. Execjs will need all of it to properly server render your components. I changed the name of the folder referenced in the components.js file because we're going to be adding flux so we'll have more than just components in our react code.

Now let's start by making a model to play with. We're going to do a simple todo app where you can cross off things on your todo list and add things to your todo list.

rails g resource Todo name:string checked:boolean

I made the root go to the todos index.

# config/routes.rb

Rails.application.routes.draw do
  resources :todos
  root 'todos#index'
end

Lets's start by adding an index action to our todo controller

# app/controllers/todos_controller.rb

class TodosController < ApplicationController
  def index
  end
end

Let's add an index file to our todos views and call a TodoIndexPage component using the react_component helper from react rails.

# app/views/todos/index.html.erb

<%= react_component('TodoIndex') %>

Now we need to make that component that we just called. We'll create the component in 'react' folder that react rails created.

# app/assets/javascripts/react/TodoIndex.js.coffee

{ div, h1 } = React.DOM

window.TodoIndex = React.createClass
  render: ->
    div {},
      h1 {}, 'Todo List'

Run your migrations, start your server, and go to localhost:3000. You should see a simple Todo List title at the top.

Pass data from rails to react

Now let's add some data to the todo list. We're going to list out all the todos on the index page and pass them to the react component using jbuilder.

# app/controllers/todos_controller.rb

class TodosController < ApplicationController
  def index
    @todos = Todo.all
  end
end

I like to create an index jbuilder file that uses a partial to keep things modular.

# app/views/todos/index.json.jbuilder

json.todos @todos, partial: 'todos/todo', as: :todo
# app/views/todos/_todo.json.jbuilder

json.extract!(todo, :id, :name, :checked)

Now let's pass it to the component and render it out in the component code.

# app/views/todos/index.html.erb

<%= react_component('TodoIndex', render(template: 'todos/index.json.jbuilder')) %>

I'm going to add lodash so I can loop over the todos passed in.

# app/assets/javascripts/react/TodoIndex.js.coffee

{ div, h1, ul, li } = React.DOM

window.TodoIndex = React.createClass
  render: ->
    div {},
      h1 {}, 'Todo List'
        _.map @props.todos, (todo)=>
          li {}, todo.name

Now I'm going to quickly add bootstrap and some visuals to make this a little cleaner.

# app/assets/javascripts/react/TodoIndex.js.coffee

{ div, h1, ul, li, a, span } = React.DOM

window.TodoIndex = React.createClass
  render: ->
    div className: 'container',
      div className: 'row',
        div className: 'col-xs-12',
          h1 {}, 'Todo List'
          ul className: 'list-unstyled',
            _.map @props.todos, (todo)=>
              li className: 'list-item', 
                a className: 'btn btn-primary', 'Check'
                span className: 'list-text', todo.name
# app/assets/stylesheets/todos.scss

.list-item {
  margin-bottom: 20px;
  .btn {
    margin-right: 10px;
  }

  &.checked {
    .btn {
      opacity: .7
    }
    .list-text {
      text-decoration: line-through;
    }
  }
}

Add a form to submit new todos using ajax without Flux

Now let's add a form and break up our list into a TodoList with TodoListItems.

# app/assets/javascripts/react/TodoIndex.js.coffee

{ div, h1, ul, li, a, span, label, input } = React.DOM

TodoForm = React.createFactory React.createClass
  render: ->
    div className: 'form-group',
      label {}, 'Enter Todo'
      input className: 'form-control', placeholder: 'Enter todo name'

TodoListItem = React.createFactory React.createClass
  render: ->
    li className: 'list-item', 
      a className: 'btn btn-primary', 'Check'
      span className: 'list-text', @props.todo.name

TodoList = React.createFactory React.createClass
  render: ->
    ul className: 'list-unstyled',
      _.map @props.todos, (todo)=>
        TodoListItem(todo: todo)

window.TodoIndex = React.createClass
  getInitialState: ->
    todos: []

  componentWillMount: ->
    @setState(todos: @props.todos)

  render: ->
    div className: 'container',
      div className: 'row',
        div className: 'col-xs-12',
          h1 {}, 'Todo List'
          TodoForm()
          TodoList(todos: @state.todos)

Now we want to add an ajax call that is triggered by the form and hits the controller to add todos. First let's start with the controller create action.

# app/controllers/todos_controller.rb

class TodosController < ApplicationController
  def index
    @todos = Todo.all
  end

  def create
    @todo = Todo.new(todo_params)
    if @todo.save
      render partial: 'todos/todo', locals: {todo: @todo} 
    else
      render json: @todo.errors.to_json
    end
  end

  private

    def todo_params
      params.require(:todo).permit(
        :name,
        :checked
      )
    end
end

Now let's update the component code so it hits this using ajax. Now when you type into the input and hit enter, it will create a todo through the todo controller.

# app/assets/javascripts/react/TodoIndex.js.coffee

{ div, h1, ul, li, a, span, label, input } = React.DOM

TodoForm = React.createFactory React.createClass
  getInitialState: ->
    todoName: ''

  onInputChange: (e)->
    @setState(todoName: e.target.value)

  onInputKeyDown: (e)->
    if e.keyCode == 13 && this.refs.todo.value.length
      @props.submitTodo(this.refs.todo.value)
      @setState(todoName: '')

  render: ->
    div className: 'form-group',
      label {}, 'Enter Todo'
      input
        onChange: @onInputChange,
        onKeyDown: @onInputKeyDown,
        ref: 'todo',
        className: 'form-control',
        placeholder: 'Enter todo name'
        value: @state.todoName

TodoListItem = React.createFactory React.createClass
  render: ->
    li className: 'list-item', 
      a className: 'btn btn-primary', 'Check'
      span className: 'list-text', @props.todo.name

TodoList = React.createFactory React.createClass
  render: ->
    ul className: 'list-unstyled',
      _.map @props.todos, (todo)=>
        TodoListItem(todo: todo)

window.TodoIndex = React.createClass
  getInitialState: ->
    todos: []

  componentWillMount: ->
    @setState(todos: @props.todos)

  componentWillMount: ->
    TodoStore.listen(@onChange)
    TodoActions.initData(@props)

  componentWillUnmount: ->
    TodoStore.unlisten(@onChange)

  onChange: (state)->
    @setState(state)

  submitTodo: (name)->
    $.ajax
      type: 'POST'
      url: '/todos'
      data:
        todo:
          name: name
          checked: false
      success: (response)=>
        @state.todos.push(response)
        @setState(todos: @state.todos)
        console.log(response)
      error: (response)=>
        console.log('error')
        console.log(response)

  render: ->
    div className: 'container',
      div className: 'row',
        div className: 'col-xs-12',
          h1 {}, 'Todo List'
          TodoForm(submitTodo: @submitTodo)
          TodoList(todos: @state.todos)

Add alt Actions and Stores to handle the flow of data

Now we can move onto adding alt. Alt is a flux framework that is really well made and simple. It takes a lot of boilerplate away and even cuts out the need for a dispatcher file. You can read more about it here, http://alt.js.org/. You're going to want to download it and include it in your vendor files along with lodash from before. Next we'll want to creat an initialize file for alt.

# app/assets/javascripts/initialize.js.coffee

window.alt = new Alt()

The components.js file will now look like this.

# app/assets/javascripts/components.js

//= require lodash/lodash.min
//= require alt/dist/alt.min
//= require initialize
//= require_tree ./react

Let's add a stores and actions folder under react and add a todo_store and todo_actions files. To start we are only going to move the initialize of data to the actions and stores.

# app/assets/javascripts/react/stores/todo_store.js.coffee

class TodoStore
  @displayName: 'TodoStore'

  constructor: ->
    @bindActions(TodoActions)
    @todos = []

    @exportPublicMethods(
      { 
        getTodos: @getTodos
      }
    )

  onInitData: (props)->
    @todos = props.todos

  getTodos: ()->
    @getState().todos

window.TodoStore = alt.createStore(TodoStore)
# app/assets/javascripts/react/actions/todo_actions.js.coffee

class TodoActions
  constructor: ->
    @generateActions(
      'initData'
    )

window.TodoActions = alt.createActions(TodoActions)

Now we need to update our TodoIndex component to properly listen to and initialize the data.

# app/assets/javascripts/react/TodoIndex.js.coffee

window.TodoIndex = React.createClass
  getInitialState: ->
    todos: []

  componentWillMount: ->
    TodoStore.listen(@onChange)
    TodoActions.initData(@props)

  componentWillUnmount: ->
    TodoStore.unlisten(@onChange)

  onChange: (state)->
    @setState(state)

  submitTodo: (name)->
    $.ajax
      type: 'POST'
      url: '/todos'
      data:
        todo:
          name: name
          checked: false
      success: (response)=>
        @state.todos.push(response)
        @setState(todos: @state.todos)
        console.log(response)
      error: (response)=>
        console.log('error')
        console.log(response)

  render: ->
    div className: 'container',
      div className: 'row',
        div className: 'col-xs-12',
          h1 {}, 'Todo List'
          TodoForm(submitTodo: @submitTodo)
          TodoList(todos: @state.todos)

Your page should work just like before but now it's using Alt.js (flux)!!! Next we're going to move the submitTodos code to the actions/store code.

We will want to take out the submitTodo code from the top level component and place that in the store code. Then we'll add a 'submitTodo' action to the TodoActions. We'll be able to remove the passing of the submitTodo down from the top level component to the form which in the long run saves a lot of code. What generally happens is you find yourself passing functions all over the place when your app grows and flux lets you put the actions directly in the correct components and the components listening to those stores will automatically update.

# app/assets/javascripts/react/stores/todo_store.js.coffee

class TodoStore
  #...

  onSubmitTodo: (name)->
    $.ajax
      type: 'POST'
      url: '/todos'
      data:
        todo:
          name: name
          checked: false
      success: (response)=>
        @todos.push(response)
        @emitChange()
      error: (response)=>
        console.log('error')
        console.log(response)

    return

  #...
# app/assets/javascripts/react/actions/todo_actions.js.coffee

class TodoActions
  constructor: ->
    @generateActions(
      'initData',
      'submitTodo'
    )

window.TodoActions = alt.createActions(TodoActions)
# app/assets/javascripts/react/TodoIndex.js.coffee

{ div, h1, ul, li, a, span, label, input } = React.DOM

TodoForm = React.createFactory React.createClass
  getInitialState: ->
    todoName: ''

  onInputChange: (e)->
    @setState(todoName: e.target.value)

  onInputKeyDown: (e)->
    if e.keyCode == 13 && this.refs.todo.value.length
      TodoActions.submitTodo(this.refs.todo.value)
      @setState(todoName: '')

  render: ->
    div className: 'form-group',
      label {}, 'Enter Todo'
      input
        onChange: @onInputChange,
        onKeyDown: @onInputKeyDown,
        ref: 'todo',
        className: 'form-control',
        placeholder: 'Enter todo name'
        value: @state.todoName

# ...

window.TodoIndex = React.createClass
  getInitialState: ->
    todos: []

  componentWillMount: ->
    TodoStore.listen(@onChange)
    TodoActions.initData(@props)

  componentWillUnmount: ->
    TodoStore.unlisten(@onChange)

  onChange: (state)->
    @setState(state)

  render: ->
    div className: 'container',
      div className: 'row',
        div className: 'col-xs-12',
          h1 {}, 'Todo List'
          TodoForm()
          TodoList(todos: @state.todos)

Add check todo functionality

Last thing we're going to do is add a checkTodo action to check off the todos. First let's add the onClick handler to the TodoListItem that will hit the checkTodo action. We'll also add the code that adds the 'checked' class if it is checked in the database. I'll be showing all of the code since this is the last thing we're going to add.

# app/assets/javascripts/react/TodoIndex.js.coffee

{ div, h1, ul, li, a, span, label, input } = React.DOM

TodoForm = React.createFactory React.createClass
  getInitialState: ->
    todoName: ''

  onInputChange: (e)->
    @setState(todoName: e.target.value)

  onInputKeyDown: (e)->
    if e.keyCode == 13 && this.refs.todo.value.length
      TodoActions.submitTodo(this.refs.todo.value)
      @setState(todoName: '')

  render: ->
    div className: 'form-group',
      label {}, 'Enter Todo'
      input
        onChange: @onInputChange,
        onKeyDown: @onInputKeyDown,
        ref: 'todo',
        className: 'form-control',
        placeholder: 'Enter todo name'
        value: @state.todoName

TodoListItem = React.createFactory React.createClass
  onCheckTodo: ->
    TodoActions.checkTodo(@props.todo.id)

  render: ->
    todoItemClasses = 'list-item'
    todoItemClasses += ' checked' if @props.todo.checked
    li className: todoItemClasses, 
      a className: 'btn btn-primary', onClick: @onCheckTodo, 'Check'
      span className: 'list-text', @props.todo.name

TodoList = React.createFactory React.createClass
  render: ->
    ul className: 'list-unstyled',
      _.map @props.todos, (todo)=>
        TodoListItem(key: "todo-#{todo.id}", todo: todo)

window.TodoIndex = React.createClass
  getInitialState: ->
    todos: []

  componentWillMount: ->
    TodoStore.listen(@onChange)
    TodoActions.initData(@props)

  componentWillUnmount: ->
    TodoStore.unlisten(@onChange)

  onChange: (state)->
    @setState(state)

  render: ->
    div className: 'container',
      div className: 'row',
        div className: 'col-xs-12',
          h1 {}, 'Todo List'
          TodoForm()
          TodoList(todos: @state.todos)

Add the checkTodo action.

# app/assets/javascripts/react/actions/todo_actions.js.coffee

class TodoActions
  constructor: ->
    @generateActions(
      'initData',
      'submitTodo',
      'checkTodo'
    )

window.TodoActions = alt.createActions(TodoActions)

Add the ajax call to 'delete' the todo in the store and emit the change to the components listening to it.

# app/assets/javascripts/react/stores/todo_store.js.coffee

class TodoStore
  @displayName: 'TodoStore'

  constructor: ->
    @bindActions(TodoActions)
    @todos = []

    @exportPublicMethods(
      { 
        getTodos: @getTodos
      }
    )

  onInitData: (props)->
    @todos = props.todos

  onSubmitTodo: (name)->
    console.log(name)
    $.ajax
      type: 'POST'
      url: '/todos'
      data:
        todo:
          name: name
          checked: false
      success: (response)=>
        @todos.push(response)
        @emitChange()
      error: (response)=>
        console.log('error')
        console.log(response)
    return false

  onCheckTodo: (todo_id)->
    $.ajax
      type: 'DELETE'
      url: "/todos/#{todo_id}"
      success: (response)=>
        _.find(@todos, { id: response.id} ).checked = true
        @emitChange()
      error: (response)=>
        console.log('error')
        console.log(response)

    return false

  getTodos: ()->
    @getState().todos

window.TodoStore = alt.createStore(TodoStore)

We'll need to add a custom 'destroy' method to the todos_controller so it just updates it with checked: true.

# app/controllers/todos_controller.rb

class TodosController < ApplicationController
  def index
    @todos = Todo.all
  end

  def create
    @todo = Todo.new(todo_params)
    if @todo.save
      render partial: 'todos/todo', locals: {todo: @todo} 
    else
      render json: @todo.errors.to_json
    end
  end

  def destroy
    @todo = Todo.find(params[:id])
    if @todo.update(checked: true)
      render partial: 'todos/todo', locals: {todo: @todo}
    else
      render json: @todo.errors.to_json
    end
  end

  private

    def todo_params
      params.require(:todo).permit(
        :name,
        :checked
      )
    end
end

Run your server and you are done! You can click the buttons and it will check the todos you add. Now you can easily add flux and react into your rails app without having to deal with things like npm and webpack.

Conclusion

I have loved using react in almost all of my projects, but I still love the utility of rails. I think this setup is one of the quickest ways for a rails dev to start playing with react code. You'll quickly find that without a flux implementation that you can't scale your react components and have them logically interact without a lot of extra code. This tutorial is a nice and simple guide that shows you how to add on flux and continue using sprockets for your react code.

Now if you are an advanced user of react and still want to integrate that into your pipeline, I think this gem isn't the right tool for you. I have been using redux a lot more lately and to really leverage all of the tooling around it, you need to be using npm. This also leads you down a path of using tools like webpack to help stay on the same ecosystem as the node developers building out the react ecosystem. If you are going down this route and still want to integrate this code into your rails app, I highly recommend the react_on_rails gem, https://github.com/shakacode/react_on_rails.

Good luck in your react adventures!!!

How to setup React Native

By Blaine Hatab | March 27, 2015

This is a quick introduction to show you how to setup react native. Quick note, I had to restart my computer after installing watchman for it to work. I'm not 100% sure why, but it worked after restarting my computer.

Install libraries

brew install node

brew install watchman

npm install -g react-native-cli

Create and start project

react-native init ExampleReactNative

cd ExampleReactNative

npm start

Launch IOS simulator

Launch Xcode, open up the project you just created, and run the project. You should see an IOS simulator pop up and now you can see the app you just created. Xcode is simply there to run the simulator and you aren't going to be using it to edit any code. Use a standard IDE or text editor to change the code. Press CMD+R on the IOS simulator to see your changes take place.

Now you're ready to make phone apps!

Links

React Native: http://facebook.github.io/react-native/

React: http://facebook.github.io/react/

3 ways to integrate Ruby on Rails + React + Flux

By Blaine Hatab | March 08, 2015

Why I’m using React with Rails

I've been using Rails for years and recently I’ve been integrating javascript frameworks into my stack in various ways to help with front end development. There are a lot of great javascript frameworks out there like Angular, Ember, Meteor, etc, but my current favorite is React and Flux. Facebook has been doing tremendous work open sourcing their javascript stack from Instagram and Facebook with tools like React, Flux, React Native, and Relay. The details behind all of those are pretty ridiculous, but I encourage you to check all of it out on their YouTube channel and React website. I’m going to focus on Flux and React in this blog, but I’d recommend these videos if you’re interested in learning about React Native and Relay, https://www.youtube.com/watch?v=KVZ-P-ZI6W4 and https://www.youtube.com/watch?v=9sc8Pyc51uU. They are really exciting libraries that will extend the already awesome React ecosystem.

React is fairly simple to get going with, but there are actually a lot of high level reasons why it is so great. It promotes events and data to be one directional constraining your development process in a good way, it leverages the virtual DOM for high performance, promotes componentized development, and is just a view layer helping to make it more modular. Now comparing React to the other frameworks isn’t fair because they are full MVC, but this is where Flux comes in. Flux is actually more of a concept than a framework and there are of Flux implementations right now, but its core principal of one directional data flow lines up with React nicely.

Enough about React!! Show me how to integrate it with Rails.

Method 1: Use React inside of Rails with react-rails

This is probably the simplest method for a Rails developer and the first thing you’ll find when Googling react and rails. There is a gem called react-rails, https://github.com/reactjs/react-rails, that gives you a handy view helper that lets you render (and server render) react components in rails views. You define your data in the Rails controller, pass it into the view helper, and it passes the data as props to the React component. It’s super simple to get going, just follow their readme and you’ll be able to get a react component in your rails views easily.

You can use either active model serializer or jbuilder to convert your data to json and you’ll be good to go in no time. Now the main reason to use this setup is you get to fully leverage your rails ecosystem and server render react easily. I mainly did this because I wanted to still use Rails for forms and authentication (devise), but I wanted react for my more dynamic pages. It works great and I can easily switch from a regular rails view for a form to a show page with React and jbuilder. I’d also recommend this blog that shows you how to setup role based authorization with this setup too, http://blog.littleblimp.com/post/109540707808/role-based-authorization-with-react-js. I found this very useful.

Here’s some sample code

# app/views/users/show.html.slim
= react_component('UserShowPage', render(template: 'users/show.json.jbuilder'))
# app/views/users/show.json.jbuilder
json.user do
  json.extract!(@user, :id, :email, :description, :github)
end

json.projects @user.projects, partial: 'projects/project', as: :project
# app/controllers/users_controller.rb

def show
   @user = User.find(params[:id])
end
# app/assets/javascripts/components/users/user_show_page.js.cjsx

window.UserShowPage = React.createClass
  render: ->
    <div>
      <ul className='list-unstyled'>
        <li>
          <b> Email: </b>
          { @props.user.email }
        </li>
        <li>
          <b> Description: </b>
          { @props.user.description }
        </li>
        <li>
          <b> Github: </b>
          { @props.user.github }
        </li>
      </ul>
      <h3> { @props.user.email + "'s Projects"}</h3>
      <ProjectList projects={ @props.projects } />
    </div>

Pros and Cons

Pretty simple to get going, but my biggest complaint is that I have to define my data on the rails side for server rendering. I would prefer to make my api calls on the javascript side and ignore the ajax call using flux if the data is already in the store. This also keeps my view specific data needs in the right place. I want the data needed for a specific view to be defined in that view code. I’m sure setting this up with react-rails is possible, but it seemed like a mountain not worth climbing. If you want to leverage server sider rendering with react-rails, you really should just pass data into the view helper.

Method 2: React/Flux front end app within Rails

This is one of the more interesting stacks and credit goes to Justin Gordon for building a template. You should check out his blog and github code for more details, https://github.com/justin808/react-webpack-rails-tutorial and http://www.railsonmaui.com/blog/2014/10/02/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/.

Now what he does is actually pretty beautiful. He has webpack compile all of the assets in the webpack folder and feed the bundle file into the rails asset pipeline. You still get to use things like turbolinks, rails gems, and all that jazz while separating your front end out into a separate eco system. He also set it up so you can easily leverage npm modules and use ES6 (which is great). He didn’t actually set this up with Flux yet, but the asset configuration is there and getting flux in there would be simple because you could take most standalone Flux apps and put it into his setup. He is actually in the works on setting it up with Flux too.

Pros and Cons

My favorite part about this setup is how it completely separates out the front end so it feels like a complete javascript app. The first setup is more like putting components inside of your rails app, while this is feeding a javascript MVC into your rails asset pipeline.

The problem I encountered was setting up server rendering, which wasn’t an issue for him. I tried to take his setup and get server rendering going with react-rails, but it started to take away from the beauty of it because I would have had to start defining data on the controllers and rails views. Then you’ve kind of broken the separation of your front end and back end by doing this. You might be able to do something fancy with react router and rendering a single page that passes different data in using the rails router, but this was another mountain I didn’t want to climb. I would definitely recommend this stack if you want to use Flux + React but keep it within a rails app for easy development.

Method 3: Separated Rails API and React/Flux front end app

This setup is conceptually simple and just involves a Rails API serving data up to a separate node app with Flux and React. You can follow any tutorials out there on building a Rails API, which will most likely leverage rails-api. https://github.com/rails-api/rails-api. Then you can follow any Flux/React tutorial that shows you how to make a standalone app and just call your Rails API for data. Egghead.io has a great set of tutorials guiding you all the way through making a Flux app.

I would highly recommend this blog if you go down this route, http://fancypixel.github.io/blog/2015/01/28/react-plus-flux-backed-by-rails-api/. They cover a lot of key concepts of getting a Rails API setup with a Flux app.

Pros and Cons

Keeping the two apps is nice conceptually, but it really makes the development process cumbersome. You’re going to be maintaining a Rails API on a server and a node app on another server if you want server rendering. If you don’t actually need server rendering, it does get simpler because you can just host on any standard web server, but it’s nice to be able to leverage server rendering if needed. If you don’t need server rendering, then I would just recommend setup two and keep your javascript app inside of your Rails app.

Conclusion

Overall I found that React being a view only library makes it easy to set it up in whatever way you need, and you have some good options depending on what your app calls for. You can easily put some React in your rails app with react-rails or go with a more complete setup with Flux inside of your Rails app or separated out.

Most of the exploration I was doing was in pursuit of a perfect stack that kept data calls in my javascript side, server rendered easily, and keep it all in one nice app. This wasn’t perfectly attainable in one setup, but I think more options will start to develop in time.

PS. Regarding Flux implementations, there are a lot out there but my current favorite is Alt. https://github.com/goatslacker/alt. I thought it was easier to reason about than the default Flux implementation and it uses ES6.

And if you are interested in defining your data in your components and can’t wait for Relay, check out this library. https://github.com/ericclemmons/react-resolver.

There's also a great community for flux and react on slack. Just go sign up here, http://reactiflux.com/.

Backbone React Sortable Table

By Blaine Hatab | December 21, 2014

This screencast shows you how to make a sortable table using react and backbone with a custom react mixin and backbone sorting. It uses backbone react component to interface backbone to react.

Sort Mixin

This is the sort mixin that is used on the component that contains the sortable table. You could rewrite this slightly and move this to the sortable table if that makes more sense to you.

# /javascripts/mixins/sort_mixin.js.coffee

MyApp.Mixins.SortMixin =

  getInitialState: ->
    { sortAttribute: @props.collection.sortAttribute, sortDirection: @props.collection.sortDirection }

  sortCollection: (attr)->
      newSortDirection = 1
      if @state.sortAttribute == attr
        newSortDirection = @state.sortDirection * -1

      @setState(sortAttribute: attr, sortDirection: newSortDirection)

    sortedData: ->

      sortedData = @getCollection()

      sortedData.sortDirection = @state.sortDirection
      sortedData.sortBy(@state.sortAttribute)
      sortedData

User Collection

The user collection defines which fields are displayed in the table, the initial sorting attribute, and the initial sorting direction. It uses Backbone's comparator method to sort the collection and exposes a sortBy method for resorting.

# /javascripts/collections/user_collection.js.coffee

MyApp.Collections.Users = Backbone.Collection.extend
  url: 'http://jsonplaceholder.typicode.com/users'
  model: MyApp.Models.User

  sortAttribute: 'name'
  sortDirection: 1

  sortTypes:
    'name' : 'Name'
    'website' : 'Website'

  sortBy: (attr) ->
    @sortAttribute = attr
    @sort()
    return


  comparator: (a, b) ->
    a = a.get(@sortAttribute)
    b = b.get(@sortAttribute)

    return 0 if a is b 
    if @sortDirection is 1
      (if a > b then 1 else -1)
    else
      (if a < b then 1 else -1)

Sortable Table Component

This is the sortable table component. It triggers the sortCollection method in the mixin when a th element is clicked. I use @getCollection() from backbone-react-component to get the sort attribute, sort types, and sort direction data so we don't have to pass those down as props.

# /javascripts/components/sortable_table.js.coffee

{thead, table, tbody, th, td, tr, i} = React.DOM

SortableTable = React.createFactory React.createClass
  mixins: [Backbone.React.Component.mixin]

  sortCollection: (sort_key)->
    @props.sortCollection(sort_key)

  render: ->
    table className: 'table table-bordered table-hover',
      thead {},
        _.map @getCollection().sortTypes, (sortTitle, sortKey) =>
          th onClick: @sortCollection.bind(@, sortKey), key: sortKey,
            sortTitle
            ' '
            if @getCollection().sortAttribute == sortKey
              if @getCollection().sortDirection == 1
                i className: 'fa fa-caret-down'
              else
                i className: 'fa fa-caret-up'

      tbody {},
        @getCollection().map (model)=>
          tr key: model.get('id'),
            _.map @getCollection().sortTypes, (sortTitle, sortKey) =>
              td key: sortKey, model.get(sortKey)

MyApp.Views.SortableTable = SortableTable

User Table Component

This is the table that is rendered into the page and contains the sortable table component. I use backbone's fetch method to get the data from the api url, which is defined in the user collection. Then I pass it into the component's props as collection so we can use backbone-react-component's @getCollection() method.

# /javascripts/components/user_table.js.coffee

{div, p, h1} = React.DOM

UserTable = React.createFactory React.createClass
  mixins: [
    Backbone.React.Component.mixin
    MyApp.Mixins.SortMixin
  ]

  render: ->
    div className: 'row',
      div className: 'col-xs-12',
        h1 {}, 'Backbone React Sortable Table Screencast'
        MyApp.Views.SortableTable(data: @sortedData(), sortCollection: @sortCollection)


users = new MyApp.Collections.Users

users.fetch().done ->
  React.render UserTable(collection: users), document.getElementById('home-page-wrapper')

Resources

github: https://github.com/openmindedinnovations/backbone_react_sortable_table_screencast

backbone-react-component: http://github.com/magalhas/backbone-react-component

Backbone: http://backbonejs.org/

React: http://facebook.github.io/react/

Middleman: http://middlemanapp.com/

Backbone React Component with Middleman

By Blaine Hatab | November 24, 2014

This is the code for the screencast showing how to integrate Backbone and React using backbone-react-component. Additionally I used coffeescript instead of jsx and middleman for the framework.

github: https://github.com/openmindedinnovations/backbone_react_component_screencast

video: http://youtu.be/iul1fWHVU6A

backbone-react-component: http://github.com/magalhas/backbone-react-component

Backbone: http://backbonejs.org/

React: http://facebook.github.io/react/

Middleman: http://middlemanapp.com/