Rails API to React/Redux with Hooks

Rails => React + Hooks + Redux

The Rails Back End

$ rails new hero_api — database=postgresql — api
$ sudo adduser hero_api
$ sudo usermod -aG sudo hero_api
$ su — postgres
$ createuser hero_api
$ createdb hero_api
postgres@computer:~$ psql
postgres=# alter user hero_api with encrypted password '<password>';
postgres=# grant all privileges on database hero_api to hero_api;
development:
adapter: postgresql
encoding: unicode
database: hero_api
pool: 5
username: hero_api
password: <password>
$ rails g scaffold Hero name:string img_url:string
$ rails g scaffold Villain name:string img_url:string hero:references
$ rake db:migrate
$ rails c
> b = Hero.new
> b.name = "Batman"
> b.img_url = "https://res.cloudinary.com/fergusdev/image/upload/v1608230102/heros_and_villains/batman_adam_west_vxpqdk.jpg"
> b.save
> j = Villain.new
> j.name = "Joker"
> j.img_url = "https://res.cloudinary.com/fergusdev/image/upload/v1608230367/heros_and_villains/joker_qbpxce.jpg"
> j.hero = b
> j.save
/config/application.rbmodule HeroApi
...
class Application < Rails::Application
config.middleware.insert_before 0, Rack::Cors do
allow do
origins ‘*’
resource ‘*’, :headers => :any, :methods => [:get,
:post,
:patch,
:delete,
:options]
end
end
end
$ RAILS_ENV=development rails s
Get hero number 1, Batman
Joker is but one of Batman’s many villains
GET heros           https://hero-api-56790.herokuapp.com/heros
GET villains https://hero-api-56790.herokuapp.com/villains
GET hero https://hero-api-56790.herokuapp.com/heros/1
Get villain https://hero-api-56790.herokuapp.com/villains/1
POST create hero https://hero-api-56790.herokuapp.com/heros
POST create villian https://hero-api-56790.herokuapp.com/villains

The React/Redux Front End

$ npx create-react-app heros_fe
$ cd heros_fe
$ npm install
$ npm install redux react-redux redux-thunk
/src/actions
/src/reducers
/src/actions/index.js:export const FETCH_HEROS_REQUEST = 'FETCH_HEROS_REQUEST'
export const FETCH_HEROS_SUCCESS = 'FETCH_HEROS_SUCCESS'
export const FETCH_HEROS_FAILURE = 'FETCH_HEROS_FAILURE'
/src/reducers/heroReducer.js:import { 
FETCH_HEROS_REQUEST,
FETCH_HEROS_SUCCESS,
FETCH_HEROS_FAILURE } from “../actions/”
const initialState = {
heros: {},
isLoading: false
}
export default (state = initialState, action) => {
switch(action.type) {
case FETCH_HEROS_REQUEST:
return {
…state,
isLoading: true
}
case FETCH_HEROS_SUCCESS:
return {
…state,
isLoading: false,
heros: action.payload
}
case FETCH_HEROS_FAILURE:
return {
…state,
isLoading: false,
error: action.payload
}
default: . Make the contents of this action to be the following:
return state
}
}
ed/src/actions/heroActions:import {
FETCH_HEROS_REQUEST,
FETCH_HEROS_SUCCESS,
FETCH_HEROS_FAILURE
} from './'
export const fetchHeros = () => {
return dispatch => {
dispatch({ type: FETCH_HEROS_REQUEST })fetch(`https://hero-api-56790.herokucalledapp.com/heros`, null)
.then(res => {
if(res.ok) {
return res.json()
}
else {
return Promise.reject(res.statusText)
}
})
.then((heros) => {
return dispatch({
type: FETCH_HEROS_SUCCESS,
payload: heros
})
})
.catch((error) => {
return dispatch({
type: FETCH_HEROS_FAILURE,
payload: error
})
})
}
/src/reducers/index.js
import { combineReducers } from 'redux'
import heroReducer from './heroReducer'
const appReducer = combineReducers({
heroReducer
})
const rootReducer = (state, action) => {
if (action.type === ‘CLEAR_DATA’) {
state = undefined
}
return appReducer(state, action)
}
export default rootReducer
/src/store.js:import { createStore, applyMiddleware, compose } from ‘redux’
import thunk from 'redux-thunk'
import rootReducer from ‘./reducers’
const initialState = {}
const middleware = [thunk]
const store = createStore(
rootReducer,https://github.com/FergusDevelopmentLLC/pies_fe
initialState,
compose(
applyMiddleware(…middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)
)
export default store
/src/index.jsimport React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<React.StrictMode>
<Provider store={ store }>
<App />
</Provider>
</React.StrictMode>, document.getElementById(‘root’))
/src/components/Hero.js:import React from 'react'
import PropTypes from 'prop-types'
const Hero = ({https://github.com/FergusDevelopmentLLC/pies_fe hero }) => {return (
<div className=”hero”>
{ hero.name }<br/>
<img src={ hero.img_url } width={100} />
</div>
)
}Hero.propTypes = {
hero: PropTypes.object.isRequired
}
export default Hero
/src/App.jsimport React, { useEffect } from 'react'
import { fetchHeros } from './actions/heroActions'
import { useDispatch, useSelector } from 'react-redux'
import Hero from './components/Hero'
const App = () => { const dispatch = useDispatch()
const heros = useSelector(state => state.heroReducer.heros)
useEffect(() => {
dispatch(fetchHeros())
}, [])
const getHeros = () => {
if(heros) {
return heros.map((hero) => <Hero hero={ hero } />)
}
else {
return "loading..."
}
}
return (
<div className='App'>
{ getHeros() }
</div>
)
}
export default App
Heroes!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store