Building a CRUD SPA with Ruby on Rails and React
Tags: react, javascript, rails, beginners
Published: 2022-01-22 20:26:58

For my React project, I built a simple CRUD SPA called the Eat Here React App. This App allows users to explore the most unusual restaurants around the world. Besides reading all restaurants, users can add new restaurants, or like and make reviews of any of them. Searching by name functionality is also implemented in the application. Here is the [demo](https://youtu.be/gSNX57lDtQg) video.

## Building a RESTful JSON API with Rails
In this project, I continued utilizing the Ruby on Rails framework for my backend API construction. I had two related models set up: a restaurant model that `has_many` reviews, a review that `belongs_to` a restaurant. I also defined a couple of `before_validation` methods for data validation. Below is the basic flow of how I built out the Rails API step by step :  

Step 1 - Create a new Rails API using the command line below. Don’t forget to add the API flag at the end.
`rails new eat-here-rails-backend --api`

Step 2 - Specify the attributes and datatypes of both models and utilize rails g resource command to create corresponding models, controllers, database migration tables, and routes.
`rails g resource Restaurant name country image gif description address`
`rails g resource Review nickname comment restaurant:belongs_to`
 
Step 3 - Define index, show, create, update, and destroy actions and serialize data in relative controllers’ actions.  
 
Step 4 - Define necessary validation and helper methods in models’ files. 
 
Step 5 -  Don’t forget to install the CORS gem and enable the CORS file to allow our server to specify from what origins it will permit. 

Here is my backend [repo](https://github.com/lena0128/eat-here-rails-api-backend) on GitHub.

## Building the Frontend App with React using Hooks
Since this was my first React project, I spent a lot of time understanding a couple of core concepts before coding. For example, the difference between props & state, React lifecycle, virtual DOM, controlled components, Redux, and React-Redux. It helped me better structure and re-factor my codes. 

During the coding process, I found the most challenging part was how to utilize react redux to read state from the store and how to update state by dispatching actions. The basic flow of using react redux is: (1) We build up our actions; (2) We dispatch the actions to the reducer; (3) The reducer returns our state. 

#### 1. Create a Store
When finishing the basic installation of all necessary dependencies, the first step is to set up the global state. Calling the `createStore` method provided by redux will return us the store object. Since I also incorporated asynchronous requests in this application, I used redux thunk as the middleware to handle all the asynchronous actions.
```
import { createStore, applyMiddleware, compose } from 'redux';
import rootReducer from '../reducers/rootReducer';
import thunk from 'redux-thunk';


const store = createStore(rootReducer, compose(applyMiddleware(thunk), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()))

export default store
``` 

#### 2. Define Reducers
Reducers are functions that accept the previous state as the first argument and an action object as the second argument and return the newly updated state. Note that reducers do not mutate the state directly. They return an entirely new state to replace the old one. Whatever the reducer returns will be our current state; The default action returns the initial state. Since I had two reducers created in my application, I utilized the `combineReducers()` function to delegate different pieces of state to each reducer.
```
import { combineReducers } from 'redux';
import restaurantsReducer from './restaurantsReducer';
import reviewsReducer from  './reviewsReducer';

const rootReducer = combineReducers({
    restaurants: restaurantsReducer,
    reviews: reviewsReducer
})

export default rootReducer

```
Below contains the code snippet of my `restaurantReducer`:
```
const initState = {
    restaurants: [], 
    loading: false 
}

const restaurantsReducer = (state = initState, action) => {
    switch(action.type){
        case "LOADING":
            return {
                ...state,
                loading: true
            }

        case "ADD_RESTAURANT":
            return {
                ...state,
                restaurants: [...state.restaurants, action.payload]
            }
            
        case "FETCH_RESTAURANTS":
            return {
                ...state,
                restaurants: [...state.restaurants, ...action.payload],
                loading: false
            }

        case "UPDATE_RESTAURANT":
            const idx = state.restaurants.findIndex((restaurant) => restaurant.id === action.payload.id)
            const restaurant = action.payload
                    return {
                        ...state,
                        restaurants: [...state.restaurants.slice(0, idx), restaurant, ...state.restaurants.slice(idx + 1) ]
                    }

        default:
            return state    
    } 
}

export default restaurantsReducer

```

#### 3. Define All Actions
An action is an object that has a type and a payload. We can imagine the payload as objects/data that we want to send to our reducer. Also, since I made fetch requests in my action creator, the thunk middleware enabled me to return functions from my action creators and pass `dispatch` as an argument to the returned functions.    
  
```
const baseUrl = "http://localhost:5000/restaurants"

export const addRestaurant = (restaurantObj) => {
    return {
        type: "ADD_RESTAURANT",
        payload: restaurantObj
    }
}

export const fetchRestaurants = () => {
     return (dispatch) => {
         dispatch({type: "LOADING"})

         fetch(baseUrl)
           .then(resp => resp.json())
           .then(data => {
               dispatch({
                   type: "FETCH_RESTAURANTS",
                   payload: data
               })
           })
     }     
}

export const createRestaurant = (restaurant) => {
    return (dispatch) => {
    const configObj = {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "Accept": "application/json"
        },
        body:JSON.stringify({restaurant})
    }
        fetch(baseUrl, configObj)
          .then(resp => resp.json())
          .then(restaurant => dispatch(addRestaurant(restaurant)))
    }
}

export const updateRestaurant = (newObject) => {
    return {
        type: "UPDATE_RESTAURANT",
        payload: newObject
    }
}

```

#### 4. Read and Update State in Relative Components
Since I used react hooks in this project, I imported `useSelector` hook to connect to the store, and imported `useDispatch` and `useEffect` hooks to read and update the state in the components. 

Feel free to check my frontend [repo](https://github.com/lena0128/eat-here-react-app) on GitHub.

## Further Thoughts
For further development, I want to add the user authentication system to this project. The challenge is how to implement the jwt auth in both Redux and Rails. I will start researching and coding this topic from this [article]("https://jwt.io/introduction/").