React with Ruby on Rails 5.1
$ ruby -v
ruby 2.4.1p111
$ rails -v
Rails 5.1.4
$ brew install yarn --without-node
$ yarn -v
1.0.2
$ rails new scoreboard --webpack=react --api
BOTH of two options. why not?
https://yarnpkg.com/en/docs/install#alternatives-tab
First let’s make API part #
we need players to render JSON
# db/seeds.rb
movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
$ rails generate model Movie name:string
$ rails db:migrate
$ rails db:seed
now ApplicationController
inherits ActionController::API
# app/controllers/movies_controller.rb
class MoviesController < ApplicationController
def index
render json: Movie.all
end
end
and route
# config/routes.rb
get "/movies", to: 'movies#index'
$ rails server -p 3001
on different terminal window,
$ curl 'http://localhost:3001/movies'
[{"id":1,"name":"Star Wars","created_at":"2017-09-15T23:47:07.706Z","updated_at":"2017-09-15T23:47:07.706Z"},{"id":2,"name":"Lord of the Rings","created_at":"2017-09-15T23:47:07.710Z","updated_at":"2017-09-15T23:47:07.710Z"}]
Looks good.
make React page #
app/javascript/packs/application.js
file says:
To reference this file, add <%= javascript_pack_tag ‘application’ %> to the appropriate layout file.
so we need views
directory.. but already did --api
$ rails generate controller home index
but this has to be changed into this
# app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
end
end
class HomeController < ActionController::Base
def index
end
end
open localhost:3001/home/index
now we have ActionController::UnknownFormat
error correctly.
make app/views/home/index.html.erb
file
there’s no error
we need this file (a view template) for React
again, app/javascript/packs/application.js
file says:
To reference this file, add <%= javascript_pack_tag ‘application’ %> to the appropriate layout file.
# app/views/home/index.html.erb
<%= javascript_pack_tag 'application' %>
open localhost:3001/home/index
and see
Hello World from Webpacker
and we have only one this page so make it root:
remove
get 'home/index'
add
root 'home#index'
now open localhost:3001/
and see
Hello World from Webpacker
on developer tools console. (type cmd + option + i
and then Console
tab)
it was successful to me.
List movies #
I wrote simple Header
and Movie
component. It looks like componentDidMount
does the job.
// app/javascript/packs/application.js
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import createReactClass from 'create-react-class'
var MOVIES = [];
function Header(props) {
return (
<div className="header">
<h1>{props.title}</h1>
</div>
);
}
Header.propTypes = {
title: PropTypes.string.isRequired
}
function Movie(props) {
return (
<div className="movie">
<div className="movie-name">
{props.name}
</div>
</div>
);
}
Movie.propTypes = {
name: PropTypes.string.isRequired
}
var Application = createReactClass({
propTypes: {
title: PropTypes.string
},
getDefaultProps: function() {
return {
title: 'Movie Theater'
};
},
getInitialState: function() {
return {
movies: this.props.initialMovies
};
},
componentDidMount: function() {
var request = new Request('/movies', {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json'
})
});
fetch(request).then(function(response){
return response.json();
}).then(function(movies){
this.setState({
movies: movies
});
}.bind(this)).catch(function(error){
console.error(error);
});
},
onChange: function(e) {
e.preventDefault();
this.setState({
movie: e.target.value
})
},
render: function() {
return (
<div className="scoreboard">
<Header title={this.props.title} />
<div>
{this.state.movies.map(function (movie, index) {
return (<Movie name={movie.name} key={movie.id} />);
}.bind(this))}
</div>
</div>
);
}
})
copy following from app/javascript/packs/hello_react.js
file
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<Application initialMovies={MOVIES} />,
document.body.appendChild(document.createElement('div')),
)
})
It’s just working! Next time I’ll do delete or create, update movies.