React.JS lets you create composable view components. Flux is an application architecture for building complex User Interfaces. It allows you to handle data flows throughout your application. It is a pattern rather than a formal framework. Unlike Angular and Knockout, the data flows in a uni-directional. What this means is that data enters through a single place (Actions) and then flow outward through the State managers (Store), finally to the View.
Why Alt?
There are several alternative implementations to Flux viz. Alt, Reflux, Fluxxor, Redux, Fluxible, et al. I will be using the Alt framework instead of Facebook's Flux. To read more about Facebook's Flux click here. My choice of going with Alt instead of Flux was based on this blog.
To quickly summarize the conclusions from the blog. Alt provides the following advantages
- Awesome helpers
- A more ES5 friendly syntax
- Better support for non-React views
- Extremely responsive community
This will be a 3 part blog. At the end of this blog we'll have a fully functional Todo App with React & Alt. In the first part we'll start with creating the backend JSON API along with listing all Todos. I am using Rails here, you should be fine using anything else as long as the data returned from the various end-points are the same. In the second part we'll deep dive into Props and state and use that knowledge to create a new Todo. In the last section we'll add the delete, clear completed and update Todo Feature.
Rails Setup
To start with a new rails project do the following
rails new rails-react-alt-todo-app -T --skip-bundle
cd rails-react-alt-todo-app
We'll clean up the comments in the Gemfile and add a couple of things. Add 'react-rails' and instead of byebug we'll be using 'pry-byebug'. I also like to use 'hirb' to format the console output, this is optional.
source 'https://rubygems.org'
gem 'rails', '4.2.5.1'
gem 'sqlite3'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 2.0'
gem 'sdoc', '~> 0.4.0', group: :doc
gem 'react-rails'
group :development, :test do
gem 'pry-byebug'
gem 'hirb'
end
group :development do
gem 'web-console', '~> 2.0'
gem 'spring'
end
Before running the migration let us bundle install and then create a new migration for the todos table. The react:install generation script creates a components.js manifest file and also creates a directory called apps/assets/javascripts/components. We will not be using this directory, we will rename this to apps/assets/javascripts/react. But we'll do it later, for now leave it as is.
bundle install
rails g react:install
rails g migration create_todos
class CreateTodos < ActiveRecord::Migration
def change
create_table :todos do |t|
t.string :name
t.boolean :completed
t.timestamps null: false
end
end
end
Cleanup the routes file and we'll start with just one route for todos#index and we'll redirect the root_path to this action. Start with creating a todos_controller with just the index action. We'll make a few changes to the application.html.erb and include a partial for the navigation bar. You can find a copy of the react.png image here, you can download it and save it under assets/images. Create a new directory /assets/fonts/ and download glypicons from here and save the aot, svg, ttf and woff extensions under /assets/fonts directory. You will need to make one change to config/applications.rb and create a new file app/assets/stylesheets/fonts.css.scss file so that the new fonts are picked up.
# config/routes.rb
Rails.application.routes.draw do
resources :todos, only: [:index]
root 'todos#index'
end
# app/controllers/todos_controller.rb
class TodosController < ApplicationController
def index
@todos = Todo.all
end
end
# add this line to the bottom of config/application.rb
config.assets.paths << "#{Rails}/vendor/assets/fonts"
While we are creating the fonts.css.scss, let us create a new scss file with our custom styles.
/* app/assets/stylesheets/fonts.css.scss */
@font-face {
font-family: 'Glyphicons Halflings';
src: asset-url('/assets/glyphicons-halflings-regular.eot');
src: asset-url('/assets/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
asset-url('/assets/glyphicons-halflings-regular.woff') format('woff'),
asset-url('/assets/glyphicons-halflings-regular.ttf') format('truetype'),
asset-url('/assets/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
}
/* app/assets/stylesheets/todos.css.scss */
.outer-container {
width: 80%;
margin: 10px auto;
}
.finished {
text-decoration: line-through;
color: lightgray;
font-style: italic;
}
.input-group {
margin: 10px 0;
}
.add-todo-group {
margin-bottom: 30px;
}
.btn-clear-group {
margin-top: 30px;
text-align: right;
}
.navbar-img {
margin-top: 5px;
}
.navbar {
border-radius: 0;
}
Let us create a layout helper that will help us include some javascripts and stylesheets, specifically bootstrap and lodash. Once that is done, we can easily reference the various version in our application layout template.
module LayoutHelper
def bootstrap_version
'3.3.6'
end
def lodash_version
'4.6.1'
end
end
<!-- views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title>Todos with React & Altlt;/title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="//netdna.bootstrapcdn.com/bootstrap/<%= bootstrap_version %>/css/bootstrap.min.css" rel="stylesheet">
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<script src="//netdna.bootstrapcdn.com/bootstrap/<%= bootstrap_version %>/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/alt/<%= alt_version %>/alt.min.js"></script>
<script src="//cdn.jsdelivr.net/lodash/<%= lodash_version %>/lodash.min.js"></script>
<%= csrf_meta_tags %>
</head>
<body>
<%= render 'shared/nav' %>
<%= yield %>
</body>
</html>
We will create a new directory under views called shared for all the partials, we'll also create a directory for all the todos templates. The view templates are as follows
<!-- views/shared/_nav.html.erb -->
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<a class="navbar-brand" href="#">Todos with React & Alt</a>
<a class="navbar-right navbar-img" href="#"><%= image_tag 'react.png' %></a>
</div>
</div>
# views/todos/_todo.json.jbuilder
json.extract!(todo, :id, :name, :completed)
# views/todos/index.json.jbulder
json.todos @todos, partial: 'todos/todo', as: :todo
# view/todos/index.html.erb
<%= react_component('TodoIndex', render(template: 'todos/index.json.jbuilder')) %>
If you start the rails server and open http://localhost:3000 you should see a blank page with the bootstrap navigation bar. We've got a lot done as far as setup goes, now let us start with our first react component. Before we do that let us seed our database so that we can list them using React.JS
# db/seeds.rb
Todo.create(name: 'Grocery shopping', completed: false)
Todo.create(name: 'Grooming', completed: true)
Todo.create(name: 'Cooking', completed: false)
Todo.create(name: 'Workout', completed: true)
Lets run the db seed and also save the alt.min.js locally so we can reference it within components.js manifest file.
rake db:seed
wget http://cdnjs.cloudflare.com/ajax/libs/alt/0.18.3/alt.min.js -O app/assets/javascripts/alt.min.js
Edit the app/assets/javascripts/components.js file and replace it with this
//= require alt.min
//= require initialize
//= require_tree ./react
Create a new file initialize.js.coffee under app/assets/javascripts as follows and rename app/assets/javascripts/components to react.
window.alt = new Alt()
With all this setup done, now we are in business. We'll start writing our React.JS components. The todo_actions.js.coffee will have a list of actions applicable for this app - let us start with just initData. The todo_store.js does all the server activities for us and we'll just start with a constructor and a couple of methods getTodos and onInitData. We'll cover props and state in depth in the second part of the blog.
todo_index.js.coffee will define the React component to create a
# app/assets/javascript/react/todo_actions.js.coffee
class TodoActions
constructor: ->
@generateActions(
'initData'
)
window.TodoActions = alt.createActions(TodoActions)
# app/assets/javascript/react/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/javascript/react/todo_index.js.coffee
{ div, h1, ul, li, a, span, label, input, button, i } = React.DOM
TodoListItem = React.createFactory React.createClass
render: ->
inputClassName = 'form-control'
div className: 'input-group input-group-lg',
span className: 'input-group-addon',
input
type: 'checkbox'
checked: @props.todo.completed
input
type: 'text'
value: @props.todo.name
className: inputClassName
span className: 'input-group-btn',
button
className: 'btn btn-danger'
type: 'button',
i
className: 'glyphicon glyphicon-remove'
TodoList = React.createFactory React.createClass
render: ->
div className: 'todos',
_.map @props.todos, (todo) =>
TodoListItem(todo: todo)
window.TodoIndex = React.createClass
getInitialState: ->
todos: []
componentWillMount: ->
TodoStore.listen(@onChange)
TodoActions.initData(@props)
onChange: (state) ->
@setState(state)
render: ->
div className: 'outer-container',
TodoList(todos: @state.todos)
When you refresh http://localhost:3000 you should see a list of todos from the seed that we ran previously as shown below

This concludes part 1 and in the next part we'll see what props and state are in React and how Alt can help considerably with the help of store and actions.