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.

 
2
Kudos
 
2
Kudos

Now read this

Rails prototype Post-It: Lesson 1

This is what I have done on week 1, course 2 of Tealeaf Academy: Database tables - schema view # posts: ‘url’, ‘title’, ‘description’ users: ‘username’ comments: ‘body’ categories: ‘name’ foreign keys (and primary keys) Migration files #... Continue →