François Catuhe
Apéro Ruby Bordeaux (Meetup)
05 december 2017
git clone git@github.com:fcatuhe/airbnb-clone.git
yarn start
yarn # shortcut for yarn install
and go to localhost:3000
Start the server
Clone the repo
Install the dependencies
(you need to have Yarn installed)
cd airbnb-clone
Good option if you
start from zero
Better suited for
mature projects
Ideal for existing
Rails applications
Once a champion Sprockets is now aging
Rails 5.1 loves Javascript
Again, 3 ways to integrate React:
cd .. # careful, get out of /airbnb-clone
rails g scaffold Flat name image_url price:integer price_currency lat:float lng:float
Install RestClient
bundle
Create the app
Scaffold the Flats
# Gemfile
gem 'rest-client'
rails new react-on-rails-demo --webpack=react --database postgresql -T
cd react-on-rails-demo
# /db/seeds.rb
Flat.destroy_all
flats_json = RestClient.get('https://raw.githubusercontent.com/lewagon/flats-boilerplate/master/flats.json')
flats_parsed = JSON.parse flats_json
flats = flats_parsed.map { |flat_parsed| flat_parsed.transform_keys(&:underscore) }
flats.each { |flat| Flat.create(flat) }
rails db:create db:migrate db:seed
Prepare to seed the flats
Create, migrate and seed
Your site is ready!
Both with Atom packages available
Copy-paste
from
to
/airbnb-clone/src
/react-on-rails-demo/app/javascript/
Import the packs (with style 😉) on your flats index
<!-- /app/views/flats/index.html.erb -->
<p id="notice"><%= notice %></p>
<h1>Flats</h1>
<div id="flats"></div>
<%= javascript_pack_tag 'flats' %>
<%= stylesheet_pack_tag 'flats' %>
...
Create the flats pack
// /app/javascript/packs/flats.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from '../App';
ReactDOM.render(<App />, document.getElementById('flats'));
Serve and try it out: localhost:3000/flats
We introduced a javascript dependency!
yarn add google-map-react
Refresh localhost:3000/flats and
Enjoy the magic!
Let's solve this with Yarn
Hey, but what if I want to make use of the amazing flats in my database instead?
All this from the amazing ShakaCode react_on_rails gem
# Gemfile
gem 'react_on_rails', '10.0.2' # prefer exact gem version to match npm version
rails generate react_on_rails:install
Watch the errors! Bundle and Yarn manually if needed
Serve and try it out: localhost:3000/hello_world
Commit all your work up to now (required by the gem)
git add .
git commit -m "Prepare for react_on_rails gem configuration"
Install the gem
bundle
Proceed with the installation
Cut-paste App.js, App.css and components folder
Create flats-bundle.js on same pattern as hello-world-bundle.js
// app/javascript/packs/flats-bundle.js
import ReactOnRails from 'react-on-rails';
import Flats from '../bundles/Flats/App';
// This is how react_on_rails can see the Flats in the browser.
ReactOnRails.register({
Flats
});
/app/javascript
/app/javascript/bundles/Flats
from
to
Modify your flats index
<!-- /app/views/flats/index.html.erb -->
<p id="notice"><%= notice %></p>
<h1>Flats</h1>
<%= react_component("Flats", props: nil, prerender: false) %>
<%= javascript_pack_tag 'flats-bundle' %>
<%= stylesheet_pack_tag 'flats-bundle' %>
...
Serve, but using Foreman now
gem install foreman # if you don't have it already installed
foreman start -f Procfile.dev
rm ./bin/webpack ./bin/webpack-dev-server
bundle binstub webpacker --standalone
Unknown switches '-w'
Temporary fix here:
Serve again and try it out: localhost:3000/flats
Well, at least nothing's broken, but weren't we promised to use the flats from our database?
# /app/controllers/flats_controller.rb
...
def index
@flats = Flat.all
flats = @flats.map do |flat|
flat.as_json.transform_keys { |key| key.camelize(:lower) }
end
@flats_props = { flats: flats }
end
...
Add a @flats_props to our flats controller index action
Pass this @flats_props to the React component in the view
<!-- /app/views/flats/index.html.erb -->
...
<%= react_component("Flats", props: @flats_props, prerender: false) %>
...
// /app/javascript/bundles/Flats/App.js
...
componentDidMount() {
const { flats } = this.props;
this.setState({
allFlats: flats,
flats,
selectedFlat: flats[0]
});
}
...
We need to modify the component to take this new prop into account, in the componentDidMount lifecycle method definition
using some ES6 destructuring and object literal shorthand syntax
Try it out: localhost:3000/flats
BOOM!
Change react_on_rails config to make use of server-bundle.js
Create server-bundle.js and import the bundles
you want to be able to server render
# /app/javascript/packs/server-bundle.js
import './hello-world-bundle';
import './flats-bundle';
# /config/initializers/react_on_rails.rb
...
config.server_bundle_js_file = "server-bundle.js"
end
In the views, allow the React components to prerender
Restart the server and play with SSR by
disabling Javascript in the Chrome dev tools settings,
for localhost:3000/hello_world and localhost:3000/flats
<!-- /app/views/flats/index.html.erb -->
...
<%= react_component("Flats", props: @flats_props, prerender: true) %>
...
<!-- /app/views/hello_world/index.html.erb -->
<h1>Hello World</h1>
<%= react_component("HelloWorld", props: @hello_world_props, prerender: true) %>
// /app/javascript/bundles/Flats/App.js
class App extends Component {
constructor(props) {
super(props);
const { flats } = props;
this.state = {
flats,
allFlats: flats,
selectedFlat: flats[0],
search: ''
};
}
...
We have no more flats, and the map is not displaying
The unbeatable 50 bucks to Stephen Grider:
📆📆📆
Rebuild the Flats component from scratch with
Sébastien Saunier, CTO Le Wagon: Youtube
10€
10€
10€
10€
10€
📆📆
📆📆📆
📆
📆📆
update rating
what you will build