How to Build a React App that Works with a Rails 5.1 API
React + Ruby on Rails = 🔥
React has taken the frontend development world by storm. It’s an excellent JavaScript library for building user interfaces. And it’s great in combination with Ruby on Rails. You can use Rails on the back end with React on the front end in various ways.
In this hands-on tutorial, we’re going to build a React app that works with a Rails 5.1 API.
You can watch a video version of this tutorial here.
To follow this tutorial, you need to be comfortable with Rails and know the basics of React.
[affiliate-section title=”Recommended Courses”][affiliate-card title=”The Best Way to Learn React for Beginners” affiliatename=”Wes Bos” text=”A step-by-step training course to get you building real world React.js + Firebase apps and website components in a couple of afternoons. Use coupon code ‘SITEPOINT’ at checkout to get 25% off.” url=”https://ReactForBeginners.com/friend/SITEPOINT” imageurl=”https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2017/07/1501203893wesbos.jpg”][/affiliate-section]
If you don’t use Rails, you can also build the API in the language or framework of your choice, and just use this tutorial for the React part.
The tutorial covers stateless functional components, class-based components, using Create React App, use of axios for making API calls, immutability-helper and more.
What We’re Going to Build
We’re going to build an idea board as a single page app (SPA), which displays ideas in the form of square tiles.
You can add new ideas, edit them and delete them. Ideas get auto-saved when the user focuses out of the editing form.
At the end of this tutorial, we’ll have a functional CRUD app, to which we can add some enhancements, such as animations, sorting and search in a future tutorial.
You can see the full code for the app on GitHub:
Setting up the Rails API
Let’s get started by building the Rails API. We’ll use the in-built feature of Rails for building API-only apps.
Make sure you have version 5.1 or higher of the Rails gem installed.
gem install rails -v 5.1.3
At the time of writing this tutorial, 5.1.3 is the latest stable release, so that’s what we’ll use.
Then generate a new Rails API app with the --api
flag.
rails new --api ideaboard-api
cd ideaboard-api
Next, let’s create the data model. We only need one data model for ideas with two fields — a title and a body, both of type string
.
Let’s generate and run the migration:
rails generate model Idea title:string body:string
rails db:migrate
Now that we’ve created an ideas table in our database, let’s seed it with some records so that we have some ideas to display.
In the db/seeds.rb
file, add the following code:
ideas = Idea.create(
[
{
title: "A new cake recipe",
body: "Made of chocolate"
},
{
title: "A twitter client idea",
body: "Only for replying to mentions and DMs"
},
{
title: "A novel set in Italy",
body: "A mafia crime drama starring Berlusconi"
},
{
title: "Card game design",
body: "Like Uno but involves drinking"
}
])
Feel free to add your own ideas.
Then run:
rails db:seed
Next, let’s create an IdeasController with an index action in app/controllers/api/v1/ideas_controller.rb
:
module Api::V1
class IdeasController < ApplicationController
def index
@ideas = Idea.all
render json: @ideas
end
end
end
Note that the controller is under app/controllers/api/v1
because we’re versioning our API. This is a good practice to avoid breaking changes and provide some backwards compatibility with our API.
Then add ideas as a resource in config/routes.rb
:
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :ideas
end
end
end
Alright, now let’s test our first API endpoint!
First, let’s start the Rails API server on port 3001:
rails s -p 3001
Then, let’s test our endpoint for getting all ideas with curl:
curl -G http://localhost:3001/api/v1/ideas
And that prints all our ideas in JSON format:
[{"id":18,"title":"Card game design","body":"Like Uno but involves drinking","created_at":"2017-09-05T15:42:36.217Z","updated_at":"2017-09-05T15:42:36.217Z"},{"id":17,"title":"A novel set in Italy","body":"A mafia crime drama starring Berlusconi","created_at":"2017-09-05T15:42:36.213Z","updated_at":"2017-09-05T15:42:36.213Z"},{"id":16,"title":"A twitter client idea","body":"Only for replying to mentions and DMs","created_at":"2017-09-05T15:42:36.209Z","updated_at":"2017-09-05T15:42:36.209Z"},{"id":15,"title":"A new cake recipe","body":"Made of chocolate","created_at":"2017-09-05T15:42:36.205Z","updated_at":"2017-09-05T15:42:36.205Z"}]
We can also test the endpoint in a browser by going to http://localhost:3001/api/v1/ideas.
Setting up Our Front-end App Using Create React App
Now that we have a basic API, let’s set up our front-end React app using Create React App. Create React App is a project by Facebook that helps you get started with a React app quickly without any configuration.
First, make sure you have Node.js and npm installed. You can download the installer from the Node.js website. Then install Create React App by running:
npm install -g create-react-app
Then, make sure you’re outside the Rails directory and run the following command:
create-react-app ideaboard
That will generate a React app called ideaboard, which we’ll now use to talk to our Rails API.
Let’s run the React app:
cd ideaboard
npm start
This will open it on http://localhost:3000.
The app has a default page with a React component called App that displays the React logo and a welcome message.
The content on the page is rendered through a React component in the src/App.js
file:
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App
Our First React Component
Our next step is to edit this file to use the API we just created and list all the ideas on the page.
Let’s start off by replacing the Welcome message with an h1
tag with the title of our app ‘Idea Board’.
Let’s also add a new component called IdeasContainer
. We need to import it and add it to the render function:
import React, { Component } from 'react'
import './App.css'
import IdeasContainer from './components/IdeasContainer'
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<h1>Idea Board</h1>
</div>
<IdeasContainer />
</div>
);
}
}
export default App
Let’s create this IdeasContainer
component in a new file in src/IdeasContainer.js
under a src/components
directory.
import React, { Component } from 'react'
class IdeasContainer extends Component {
render() {
return (
<div>
Ideas
</div>
)
}
}
export default IdeasContainer
Let’s also change the styles in App.css
to have a white header and black text, and also remove styles we don’t need:
.App-header {
text-align: center;
height: 150px;
padding: 20px;
}
.App-intro {
font-size: large;
}
This component needs to talk to our Rails API endpoint for getting all ideas and display them.
Fetching API Data with axios
We’ll make an Ajax call to the API in the componentDidMount()
lifecycle method of the IdeasContainer component and store the ideas in the component state.
Let’s start by initializing the state in the constructor with ideas as an empty array:
constructor(props) {
super(props)
this.state = {
ideas: []
}
}
And then we’ll update the state in componentDidMount()
.
Let’s use the axios library for making the API calls. You can also use fetch or jQuery if you prefer those.
Install axios with npm:
npm install axios --save
Then import it in IdeasContainer
:
import axios from 'axios'
And use it in componentDidMount()
:
componentDidMount() {
axios.get('http://localhost:3001/api/v1/ideas.json')
.then(response => {
console.log(response)
this.setState({ideas: response.data})
})
.catch(error => console.log(error))
}
Now if we refresh the page … it won’t work!
We’ll get a “No Access-Control-Allow-Origin header present” error, because our API is on a different port and we haven’t enabled Cross Origin Resource Sharing (CORS).
Enabling Cross Origin Resource Sharing (CORS)
So let’s first enable CORS using the rack-cors gem in our Rails app.
Add the gem to the Gemfile:
gem 'rack-cors', :require => 'rack/cors'
Install it:
bundle install
Then add the middleware configuration to config/application.rb
file:
config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'http://localhost:3000'
resource '*', :headers => :any, :methods => [:get, :post, :put, :delete, :options]
end
end
We restrict the origins to our front-end app at http://localhost:3000
and allow access to the standard REST API endpoint methods for all resources.
Now we need to restart the Rails server, and if we refresh the browser, we’ll no longer get the CORS error.
The page will load fine and we can see the response data logged in the console.
So now that we know we’re able to fetch ideas from our API, let’s use them in our React component.
We can change the render function to iterate through the list ideas from the state and display each of them:
render() {
return (
<div>
{this.state.ideas.map((idea) => {
return(
<div className="tile" key={idea.id} >
<h4>{idea.title}</h4>
<p>{idea.body}</p>
</div>
)
})}
</div>
);
}
That will display all the ideas on the page now.
Note the key
attribute on the tile div.
We need to include it when creating lists of elements. Keys help React identify which items have changed, are added, or are removed.
Now let’s add some styling in App.css
to make each idea look like a tile:
.tile {
height: 150px;
width: 150px;
margin: 10px;
background: lightyellow;
float: left;
font-size: 11px;
text-align: left;
}
We set the height, width, background color and make the tiles float left.
Stateless functional components
Before we proceed, let’s refactor our code so far and move the JSX for the idea tiles into a separate component called Idea
.
import React from 'react'
const Idea = ({idea}) =>
<div className="tile" key={idea.id}>
<h4>{idea.title}</h4>
<p>{idea.body}</p>
</div>
export default Idea
This is a stateless functional component (or as some call it, a “dumb” component), which means that it doesn’t handle any state. It’s a pure function that accepts some data and returns JSX.
Then inside the map function in IdeasContainer
, we can return the new Idea component:
{this.state.ideas.map((idea) => {
return (<Idea idea={idea} key={idea.id} />)
})}
Don’t forget to import Idea
as well:
import Idea from './Idea'
Great, so that’s the first part of our app complete. We have an API with an endpoint for getting ideas and a React app for displaying them as tiles on a board!
Adding a new record
Next, we’ll add a way to create new ideas.
Let’s start by adding a button to add a new idea.
Inside the render function in IdeasContainer
, add:
<button className="newIdeaButton">
New Idea
</button>
And let’s add some styling for it in App.css
:
.newIdeaButton {
background: darkblue;
color: white;
border: none;
font-size: 18px;
cursor: pointer;
margin-right: 10px;
margin-left: 10px;
padding:10px;
}
Now when we click the button, we want another tile to appear with a form to edit the idea.
Once we edit the form, we want to submit it to our API to create a new idea.
Continue reading %How to Build a React App that Works with a Rails 5.1 API%
LEAVE A REPLY
You must be logged in to post a comment.