Reputation: 65
Ok, so here is my issue. I have this REACT App and so far what I have is working perfectly when I run it on my local host on my computer. I have it deployed at Heroku, and the app loads but reacts very differently then it does on my local host. Here is what's going on.
On my Local Host, when the app loads the main page, on the navbar, there are several options to pick from, including Tickets. If you click on the Tickets option, it loads up the ticket page. From there, on the left side of the page, you have the option to Enter in a new ticket and save it to a Mongo Database. On the right side of the page it lists all current tickets that have been entered and are stored in the Mongo Database. You can click on one of those tickets to view the ticket details or you also have the option to delete the ticket from the Database. Like I said, it's all working fine ran locally.
The problem is, when I load up the deployed version from Heroku, it loads the main page, but when I click on the Ticket option in the Navbar, instead of loading the Tickets page, it just returns all of the excising tickets in JSON format. It actually does this for all the options on the Navbar (Customers, Materials and Tickets.) I've been messing with my routes trying to find the issue and have not been successful. I'm kind of guessing/hoping this is something simple that I'm missing, but part of my problem is, I have a lot of code and I'm not sure what I need to post here. So I'm going to post my Server.js, and some routes and I guess go from there.
Server.js
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const routes = require("./routes");
const app = express();
const port = process.env.PORT || 3030;
const customers = require('./routes/api/Customers');
const materials = require('./routes/api/Materials');
const tickets = require('./routes/api/Tickets');
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
}
const DB = process.env.DB;
// app.use('/', customers);/
app.use('/customers', customers);
app.use('/materials', materials)
app.use('/tickets', tickets)
mongoose
.connect(DB,{ useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false})
.then(() => console.log('MongoDB Connected!'))
.catch(err => console.log(err));
app.listen(port, () => console.log(`Server started on port ${port}`));
routes>api>index.js
const path = require("path");
const router = require("express").Router();
const apiRoutes = require("./api");
// API Routes
router.use("/api", apiRoutes);
// If no API routes are hit, send the React app
router.use(function(req, res) {
res.sendFile(path.join(__dirname, "../client/build/index.html"));
});
module.exports = router;
routes>api>Tickets.js
onst express = require('express');
const router = express.Router();
const ticketController = require('../../controllers/ticket');
router.route('/')
.get(ticketController.index)
.post(ticketController.newTicket);
router.route('/:ticketID')
.get(ticketController.getTicket)
.put(ticketController.replaceTicket)
.patch(ticketController.updateTicket)
.delete(ticketController.removeTicket);
module.exports = router;
Here is code from the React App:
API.js
import axios from "axios";
export default {
// Gets all tickets
getTickets: function() {
return axios.get("/Tickets");
},
// Gets the ticket with the given id
getTicket: function(id) {
return axios.get("/Tickets/" + id);
},
// Deletes the ticket with the given id
deleteTicket: function(id) {
return axios.delete("/Tickets/" + id);
},
// Saves a ticket to the database
saveTicket: function(ticketData) {
return axios.post("/Tickets", ticketData);
},
// Gets all Customers
getCustomers: function() {
return axios.get("/Customers");
},
// Gets the customer with the given id
getCustomer: function(id) {
return axios.get("/Customers/" + id);
},
getCustLocations: function(id) {
return axios.get("/Customers/" + id + "/locations");
},
// Deletes the customer with the given id
deleteCustomer: function(id) {
return axios.delete("/Customers/" + id);
},
// Saves a customer to the database
saveCustomer: function(customerData) {
return axios.post("/Customers", customerData);
},
// Gets all Materials
getMaterials: function() {
return axios.get("/Materials");
},
// Gets the Material with the given id
getMaterial: function(id) {
return axios.get("/Materials/" + id);
},
// Deletes the Material with the given id
deleteMaterial: function(id) {
return axios.delete("/materials/" + id);
},
// Saves a Material to the database
saveMaterial: function(materialData) {
return axios.post("/materials", materialData);
},
};
Tickets.js
import React, { useState, useEffect } from "react";
import DeleteBtn from "../DeleteBtn";
import Jumbotron from "../Jumbotron";
import API from "../../utils/API";
import { Link } from "react-router-dom";
import { Col, Row, Container } from "../Grid";
import { List, ListItem } from "../List";
import { Input, FormBtn } from "../Form";
const moment = require('moment');
function Tickets(props) {
// Setting our component's initial state
const [tickets, setTickets] = useState([])
const [formObject, setFormObject] = useState({})
const [customers, setCustomers] = useState([])
const [materials, setMaterials] = useState([])
console.log("Customer = ", customers)
// Load all tickets and store them with setTickets
useEffect(() => {
loadTickets()
}, [])
// Loads all tickets and sets them to tickets
function loadTickets() {
API.getTickets()
.then(res =>
setTickets(res.data)
)
.catch(err => console.log(err));
};
// Load all Customers and store them with setCustomer
useEffect(() => {
loadCustomers()
}, [])
// Loads all customers and sets them to customers
function loadCustomers() {
API.getCustomers()
.then(res =>
setCustomers(res.data)
)
.catch(err => console.log(err));
};
// Load all materials and store them with setMaterials
useEffect(() => {
loadMaterials()
}, [])
// Loads all materials and sets them to customers
function loadMaterials() {
API.getMaterials()
.then(res =>
setMaterials(res.data)
)
.catch(err => console.log(err));
};
// Deletes a ticket from the database with a given id, then reloads tickets from the db
function deleteTicket(id) {
API.deleteTicket(id)
.then(res => loadTickets())
.catch(err => console.log(err));
}
// Handles updating component state when the user types into the input field
function handleInputChange(event) {
const { name, value } = event.target;
setFormObject({...formObject, [name]: value})
};
// When the form is submitted, use the API.saveTicket method to save the ticket data
// Then reload tickets from the database
function handleFormSubmit(event) {
event.preventDefault();
if (formObject.ticketDate) {
API.saveTicket({
ticketDate: formObject.ticketDate,
ticketNum: formObject.ticketNum,
ticketCust: formObject.ticketCust,
ticketMaterial: formObject.ticketMaterial,
ticketTareWeight: formObject.ticketTareWeight,
ticketGrossWeight: formObject.ticketGrossWeight,
ticketNetWeight: formObject.ticketGrossWeight - formObject.ticketTareWeight
})
.then(res => loadTickets())
.catch(err => console.log(err));
document.getElementById("ticketFrm").reset();
setFormObject({})
}
};
return (
<Container fluid>
<Row>
<Col size="md-6">
<Jumbotron>
<h1>Add Ticket</h1>
</Jumbotron>
<form id="ticketFrm">
<Input
onChange={handleInputChange}
name="ticketDate"
placeholder="Date"
/>
<Input
onChange={handleInputChange}
name="ticketNum"
placeholder="Ticket Number (required)"
/>
<select onChange={handleInputChange}
name="ticketCust"
style={{width: '100%', height: 35, marginBottom: 15}}>
{customers.map((customers, ii ) => (
<>
<option key={ii} value="" hidden>Select Customer</option>
<option key={customers.custName}>{customers.custName}</option>
</>
))}
</select>
<select onChange={handleInputChange}
name="ticketMaterial"
style={{width: '100%', height: 35, marginBottom: 15}}>
{materials.map((materials, ii ) => (
<>
<option key={ii} value="" hidden>Select Material</option>
<option key={materials._id}>{materials.name}</option>
</>
))}
</select>
<Input
onChange={handleInputChange}
name="ticketTareWeight"
placeholder="Tare Weight"
/>
<Input
onChange={handleInputChange}
name="ticketGrossWeight"
placeholder="Gross Weight"
/>
<FormBtn
disabled={!(formObject.ticketNum)}
onClick={handleFormSubmit}>
Submit Ticket
</FormBtn>
</form>
</Col>
<Col size="md-6 sm-12">
<Jumbotron>
<h1>Current Tickets</h1>
</Jumbotron>
{tickets.length ? (
<List>
{tickets.map(tickets => (
<ListItem key={tickets._id}>
<Link to={"/Tickets/" + tickets._id}>
<strong>
Ticket Date - {moment(tickets.ticketDate).format("MM-DD-YYYY")}
<br></br>
Ticket# - {tickets.ticketNum}
<br></br>
{tickets.ticketCust}
</strong>
</Link>
<DeleteBtn onClick={() => deleteTicket(tickets._id)}/>
</ListItem>
))}
</List>
) : (
<h3>No Results to Display</h3>
)}
</Col>
</Row>
</Container>
);
}
export default Tickets;
APP.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import NavBar from './components/NavBar';
import Tickets from './components/pages/Tickets';
import TicketDetails from './components/pages/TicketDetails';
import Customers from './components/pages/Customers';
import CustomerDetails from './components/pages/CustomerDetails';
import Materials from './components/pages/Materials';
import MatDetails from './components/pages/MatDetails';
import 'bootstrap/dist/css/bootstrap.min.css';
import '../src/components/App.css';
function App() {
return (
<Router>
<div>
<NavBar/>
<Switch>
<Route exact path={"/tickets"}>
<Tickets />
</Route>
<Route exact path={"/tickets/:id"}>
<TicketDetails />
</Route>
<Route exact path={"/customers"}>
<Customers />
</Route>
<Route exact path={"/customers/:id"}>
<CustomerDetails />
</Route>
<Route exact path={"/materials"}>
<Materials />
</Route>
<Route exact path={"/materials/:id"}>
<MatDetails />
</Route>
</Switch>
</div>
</Router>
);
}
export default App;
NavBar.js
import React, { useState } from 'react';
import {
Collapse,
Navbar,
NavbarToggler,
NavbarBrand,
Nav,
NavItem,
NavLink,
NavbarText
} from 'reactstrap';
const NavBar = (props) => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return (
<div>
<Navbar color="dark" dark expand="md">
<NavbarBrand href="/">HAMMER</NavbarBrand>
<NavbarToggler onClick={toggle} />
<Collapse isOpen={isOpen} navbar>
<Nav className="mr-auto" navbar>
<NavItem>
<NavLink href="/Tickets">Tickets</NavLink>
</NavItem>
<NavItem>
<NavLink href="/Customers">Customers</NavLink>
</NavItem>
<NavItem>
<NavLink href="/Materials">Materials</NavLink>
</NavItem>
</Nav>
<NavbarText >Nathan Huber</NavbarText>
</Collapse>
</Navbar>
</div>
);
}
export default NavBar;
I appreciate any help!
Thanks,
Nathan
Upvotes: 0
Views: 96
Reputation: 65
Turns out, I had an issue with my build folder. I also edited a my server.js file. I changed:
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
}
To:
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
app.get("/*", function(req, res) {
res.sendFile(path.join(__dirname, "./client/build/index.html"));
});
}
else {
app.use(express.static(path.join(__dirname, '/client/public')));
app.get("/*", function(req, res) {
res.sendFile(path.join(__dirname, "./client/public/index.html"));
});
}
This seems to correct my route issue, but also seems to have opened a whole new can of worms, lol. Now when I click on the Tickets link in the NavBar it seems to open the Tickets page, but then it disappears and throws a bunch of errors. I'm assuming the router is working since it starts to open the page at least. I'll work on these new errors and submit a new question if I need to.
Thanks for the help.
-N8
Upvotes: 1