Skip to content

Instantly share code, notes, and snippets.

@romchambe
Last active July 20, 2018 12:02
Show Gist options
  • Save romchambe/5c472f5a3caed707667a7de8429397b9 to your computer and use it in GitHub Desktop.
Save romchambe/5c472f5a3caed707667a7de8429397b9 to your computer and use it in GitHub Desktop.
Setup a React app with a Rails API

Initialization

  1. Set the Rails API up as described in this guide

  2. Create a React client app using $ create-react-app inside the root folder of the Rails app

  3. Set up local servers using the Foreman gem

    • add gem foremanin the development group of your Gemfile and run command $ bundle install
    • Create a Procfile.dev file and add the following lines so that both the rails server and the react server start (on different ports) when running $ foreman start -f Procfile.dev:
    web: sh -c 'cd client && PORT=3001 npm start'
    api: bundle exec rails s -p 3000
    
    • Optionally, create a rake.start file in the lib/tasks folder, to have a nicer command to start both servers. Add the following lines to rake.start:
    namespace :start do
      task :development do
        exec 'foreman start -f Procfile.dev'
      end
    
      desc 'Start production server'
      task :production do
        exec 'NPM_CONFIG_PRODUCTION=true npm run postinstall && foreman start'
      end
    end
    
    desc 'Start development server'
    task :start => 'start:development'

Enabling CORS

Following this tutorial frome (Sitepoint)[https://www.sitepoint.com/react-rails-5-1/]

  1. We first need to add the rack-cors gem to our Gemfile:
gem 'rack-cors', :require => 'rack/cors'
  1. We allow requests with certain origin to our API by adding the following bit to config/application.rb
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

Routing

  1. Install the React router inside the client/ folder using $ npm install --save react-router-dom (the --save flag is to persist the package as a dependency in package.json
  2. Change the index.js file of the client app to look like this:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './components/App';

ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'));
  1. To setup the routing, 2 main components can be used:
    • <Link> will modify the URL. can be used so that the link as a different style if the current path matches the link path. More info
    • <Route> will tell the app which view should be rendered in a given area of the UI for a given URL. Hence, the route component should be placed where a given component is expected to render. More info

Routing with Redux

To manipulate routes in redux reducers, it is necessary to use a specific router component compatible with react-router-dom, and that is provided by connected-react-routerlibrary:

  1. Install the package: $ npm install --save connected-react-router
  2. Create a history object. That's the history object that will show in our redux store.
  3. Wrap the root reducer with connectRouter and supply the history object to get a new root reducer that will be able to talk with the ConnectedRouter component
  4. Use routerMiddleware(history) to dispatch history actions.
  5. Add the ConnectedRouter component inside the Provider component.
import { createBrowserHistory } from 'history'
import { applyMiddleware, compose, createStore } from 'redux'
import { connectRouter, routerMiddleware, ConnectedRouter } from 'connected-react-router'
...
const history = createBrowserHistory()

const store = createStore(
  connectRouter(history)(rootReducer), // new root reducer with router state
  initialState,
  compose(
    applyMiddleware(
      routerMiddleware(history), // for dispatching history actions
      // ... other middlewares ...
    ),
  ),
)

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}> { /* place ConnectedRouter under Provider */ }
      <div> { /* your usual react-router v4 routing */ }
        <Switch>
          <Route exact path="/" render={() => (<div>Match</div>)} />
          <Route render={() => (<div>Miss</div>)} />
        </Switch>
      </div>
    </ConnectedRouter>
  </Provider>,
  document.getElementById('react-root')
)

Routing with Redux if Routes parent component is using connect

In case the parent component of the routes is using connect to access the redux store, we need to use a special function provided by React Router so that the Routes work (if not, they will not render the component they are supposed to, but not returning an error). This function is withRouter. Details about implementation

React Router v4 guide

Styles

CSS preprocessing

  1. Install the preprocessor recommended by React: $ npm install --save node-sass-chokidar
  2. Rename the source css files with scss extension
  3. Install npm-run-all to run all the scripts in package.json. We will add scripts so that our scss files are watched and reprocessed on an ongoing basis. Run the command: $ npm install --save npm-run-all
  4. Add the scripts to package.json (+ means the line needs to be added, - means it needs to be removed):
+    "build-css": "node-sass-chokidar src/ -o src/",
+    "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
-    "start": "react-scripts start",
-    "build": "react-scripts build",
+    "start-js": "react-scripts start",
+    "start": "npm-run-all -p watch-css start-js",
+    "build-js": "react-scripts build",
+    "build": "npm-run-all build-css build-js",

Bootstrap

Links to the CDN:

CSS

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

JS

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

Debugging

Redux store

  • Select the Provider component in React tab
  • In the console: $r.store.getState();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment