Flatiron School — Project 5 (React Redux)

Benjamin Cheng
5 min readOct 12, 2021

As my coding bootcamp journey nears the end, we get the opportunity to tie all the fundamentals together into a single project. This project mainly focuses on using a Ruby on Rails API backend with a React-Redux frontend application. As an avid gamer of League of Legends, I decided to make the project based on it. This app allows users to keep track of their favorite playable champions and shows the general meta (trend of champions) of the game.

Ruby on Rails API Backend

The application used RoR API to keep a database of the user data, champion’s names, their respective lane, image, and allows users to keep track of their favorite champions, etc. The collected data also allows the application to show the top performing/top picked champions to let players know what to pick to play for the competitive scene.

Models

This starts by creating User and Champion models and associating them with each other with a joint table called ChampionOwnership. The below is the code to implement it:

  1. User
class User < ApplicationRecordhas_secure_passwordhas_many :champion_ownerships, dependent: :destroyhas_many :champions, through: :champion_ownershipsend

2. Champion

class Champion < ApplicationRecordhas_many :champion_ownerships, dependent: :destroyhas_many :users, through: :champion_ownershipsend

3. ChampionOwnership

class ChampionOwnership < ApplicationRecordbelongs_to :userbelongs_to :championend

Controllers

Along with models, action controllers were created: users, champions, championOwnership, and sessions. These are responsible for knowing which controller and action to process depending the request. The user controller is shown below as an example:

class Api::V1::UsersController < ApplicationControllerbefore_action :set_user, only: [:show, :update, :destroy]def index@users = User.allrender json: @usersenddef showrender json: @user, include: ['champions.users']enddef create@user = User.new(user_params)if @user.savesession[:user_id] = @user.idrender json: @userelserender json: @user.errors, status: :unprocessable_entityendend
def update
if @user.update(user_params)render json: @userelserender json: @user.errors, status: :unprocessable_entityendenddef destroy@user.destroyendprivatedef set_user@user = User.find(params[:id])enddef user_paramsparams.require(:user).permit(:username, :email, :password)endend

Serializer

Active model serializer allows the application to build serializable hashes JSON objects. This implementation generates serializiers for our models and can be called implicitly for a specific object for specific tasks. A readable serialized hash of a champion is shown below:

CORS

Cross-Origin Resource Sharing is a HTTP-header based mechanism that allows a resource to be requested from an outside the server’s domain. Requests such as [:get, :post, :put, :patch, :delete, :options, :head] is configured at config/initializers/cors.rb and allows the request to the API to be called.

Rails.application.config.middleware.insert_before 0, Rack::Cors doallow doorigins 'http://localhost:3001'resource '*',headers: :any,methods: [:get, :post, :put, :patch, :delete, :options, :head],credentials: trueendend

Active Record

Active Record works with the model that handles the data and logic of the application. This system facilitates the creation and the use of the object data that is stored in the database. This implementation is called Object Relational Mapping System. Example of creation of the database table for Users is as:

class CreateUsers < ActiveRecord::Migration[6.0]def changecreate_table :users do |t|t.string :usernamet.string :emailt.string :password_digestt.timestampsendendend

React Redux

Now with the backend of the app set up, we can start to create the front end of the application where all of the request and UI. Use npx create-react-app lol-rate --template redux to create the initial template. It creates a single-page React application starter. React-Redux allows the React compoenets to read data from the Redux store and let’s you dispatch actions to the store to update data.

State Management

This is used to faciliate communication and sharing data across components in Redux. The data is represented as the state of your app that can be read and write to. This can be seen in currentUser.js where the state of the champion is retrived and updated for the champions that belongs to the current user.

import store from '../store'export const getMyChampions = user => {return dispatch => {return fetch(`http://localhost:3000/api/v1/users/${user.id}`, {credentials: "include",method: "GET",headers: { "Content-Type": "application/json" }}).then(r => r.json()).then(u => {if (u.error) {alert(u.error);} else {let t = store.getState().championsReducerdispatch(setMyChampions(u, t.champions));dispatch(markAsClaimed(u, t.champions));}});};};

Redux-Thunk Middleware

Redux-Thunk is the middleware used to handle asynchronous actions in Redux. Action creators do not support asynchronous actions such as fetching data so we can utilize Redux Thunk. Using this middleware avoids running into a problem when the action creator returns the action before it is fetched. as seen below.

// //Asynchronous action creatorsexport const createChampion = champion => {return dispatch => {return fetch("http://localhost:3000/api/v1/champions", {credentials: "include",method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify(champion)}).then(r => r.json()).then(champion => {dispatch(addChampion(champion));dispatch(resetChampionForm());});}}

Router

React Router let’s a single page application to navigate through the multiple views without having the page to reload each time. In the route component of our App.js, the path and the component the use sees when they navigate to the path as seen below:

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';...class App extends React.Component {...render() {...return (<div><Router history={history}><div className="App"><NavBar />... <Route exact path="/" component={Home} /> <Route path="/champions" component={Champions} /> <Route path="/Like" component={Like} /> <Route path="/about" component={About} />...</Router>);}}

Reducer

Reducers provide a way to update an application’s state using an action as arguments and returns a new state. It is a pure function that works with a state and holds the data the component requires and it tells what a component renders. Below is an example of the signUpForm reducer for this application.

const initialState = {username: "",password: "",email: ""}export default (state = initialState, action) => {switch (action.type) {case "UPDATE_SIGNUP_FORM":return action.formDatacase "RESET_SIGNUP_FORM":return initialStatedefault:return state}}

All in all, this project covered the basic fundamental of React Redux and was a great learning experience to jump start my coding career with more to come.

--

--