For part 2, I will be adding onto the nascent full stack app described last week here: https://benton-westergaard.medium.com/build-a-simple-fullstack-app-using-rails-and-react-460ebec3a1bc

The goal for this blog is to demonstrate how a user can add a new locations to the sportsball app, which includes all major league teams (baseball, basketball, football, hockey), indicates whether any team has the state’s capitol city in their name (e.g., ‘Denver’ Broncos, therefore this boolean would be ‘true.’). Below is how the app appears without making any changes:

First step to adding the new location form, is to add a file in the components folder titled LocationForm.js. Because the form will be getting value from state, the easiest (although not the only) way to do this is to use a Class (vs functional) component.

import React, { Component } from 'react'const initialState = {name: "",baseball: "",basketball: "",football: "",hockey: "",capital: false,total_teams: 0,}export default class LocationForm extends Component{state = initialStaterender() {return(<form className="todo-form><h2>Add a new Location</h2><label>Name (US State)</label><input type="text" name="name" /><label>Baseball Team(s)</label><input type="text" name="baseball" /><label>Basketball Team(s)</label><input type="text" name="basketball" /><label>Football Team(s)</label><input type="text" name="football" /><label>Hockey Team(s)</label><input type="text" name="hockey" /><label>US State Capital in Any Names?</label><input type="checkbox" name="capital" /><label>Total Major League Teams</label><input type="number" name="number" /><input type="submit" /></form>)}}

Also, in App.js, I import the new form using:

import LocationForm from './components/LocationForm';

Right now, I’ll adds it about the location container in the existing render form:

render() {return (<div className="App"><h1>Sportsball App</h1><LocationForm /><LocationContainer locations={this.state.locations} /></div>);}}

Before making the form work, I’m going to add some styling, mainly because even though I can accept a hideous form, there is so much going on that I doubt anyone can see what I’m trying to do! So, here is some basic styling:

body {background-color: rgb(250, 248, 248);}.App{text-align: center;}.location-form {display: flex;flex-direction: column;width: 300px;height: 400px;margin: auto;justify-content: space-between;background-color: white;padding: 20px;border-radius: 6px;margin-bottom: 20px;box-shadow: 0 20px 10px -10px lightblue;}.location-list {display: flex;flex-flow: row wrap;list-style-type: none;justify-content: space-between;width: 90%;margin: 0 auto;padding-inline-start: 0;}.location-item {background-color: white;width: 300px;height: auto;box-shadow: 0 20px 10px -10px lightblue;border-radius: 6px;margin-top: 2rem;}.capital-input {display: flex;justify-content: center;}

Which, while still pretty hideous, at least makes the form not overlap the existing items:

Now, to make the form function, I need to make the form gets it’s values from state, for example, {this.state.baseball}. However to save some typing, I will destructure the object by setting it equal to state:

render() {const {location, baseball, basketball, football, hockey, capital, total_teams} = this.statereturn(<form className="location-form"><h2>Add a new Location</h2><label>Name (US State)</label><input type="text" name="name" value={location} /><label>Baseball Team(s)</label><input type="text" name="baseball" value={baseball}/><label>Basketball Team(s)</label><input type="text" name="basketball" value={basketball} /><label>Football Team(s)</label><input type="text" name="football" value={football}/><label>Hockey Team(s)</label><input type="text" name="hockey" value={hockey}/><div className="capital-input" ><label>US State Capital in Any Names?</label><input type="checkbox" name="capital" checked={capital}/></div><label>Total Major League Teams</label><input type="number" name="number" value={total_teams}/><input type="submit" /></form>)}}

Now, I need to add some functionality to App.js to pass down the function:

import React, { Component } from 'react';import './App.css';import LocationContainer from './components/LocationContainer';import LocationForm from './components/LocationForm';const locationsURL = "http://localhost:3000/locations";class App extends Component {state = {locations: []}//Make add, delete, update methodsdeleteLocation = () => {}componentDidMount(){this.getLocations()}getLocations = () => {fetch(locationsURL).then(response => response.json()).then(locations => this.setState({locations}))}addLocation = (newLocation) => {this.setState({locations: [...this.state.locations, newLocation]})fetch(locationsURL, {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify(newLocation)})}render() {return (<div className="App"><h1>Sportsball App</h1><LocationForm addLocation={this.addLocation} /><LocationContainer locations={this.state.locations} /></div>);}}export default App;

And of course, the new location and handleChange to the LocationForm.js file:

import React, { Component } from 'react'const initialState = {name: "",baseball: "",basketball: "",football: "",hockey: "",capital: false,total_teams: 0,}export default class LocationForm extends Component{state = initialStatehandleChange = (event) => {let {name, value, checked} = event.targetvalue = (name === "capital") ? checked : valuethis.setState({[name]: value})}handleSubmit = (event) => {event.preventDefault()this.props.addLocation(this.state)}render() {const {location, baseball, basketball, football, hockey, capital, total_teams} = this.statereturn(<form className="location-form" onSubmit={this.handleSubmit}><h2>Add a new Location</h2><label>Name (US State)</label><input type="text" name="name" value={location} onChange={this.handleChange}/><label>Baseball Team(s)</label><input type="text" name="baseball" value={baseball} onChange={this.handleChange}/><label>Basketball Team(s)</label><input type="text" name="basketball" value={basketball} onChange={this.handleChange}/><label>Football Team(s)</label><input type="text" name="football" value={football} onChange={this.handleChange}/><label>Hockey Team(s)</label><input type="text" name="hockey" value={hockey} onChange={this.handleChange}/><div className="capital-input" ><label>US State Capital in Any Names?</label><input type="checkbox" name="capital" checked={capital} onChange={this.handleChange}/></div><label>Total Major League Teams</label><input type="number" name="total_teams" value={total_teams} onChange={this.handleChange} /><input type="submit" /></form>)}}

And finally, need to add a new method on the backend to handle the creating of a new location/persist it:

class LocationsController < ApplicationControllerdef index@locations = Location.allrender json: @locationsenddef create@location = Location.create(name: params[:name],baseball: params[:baseball],basketball: params[:basketball],football: params[:football],hockey: params[:hockey],capital: params[:capital],total_teams: params[:total_teams])render json: @location, status: :createdendend

Link to front end repo: https://github.com/bwesterg/sportsball-frontend

link to back end repo: https://github.com/bwesterg/sportsball_backend

--

--