Author:
Last Updated: 1 January, 2023
📊 Table of Contents
- 📊 Table of Contents
- 🥅 The Goal
- 📦 Setup
- 🎞️ Creating a Gallery
- 🔌 Connecting the Backend
- Install Axios
- GET All Members
- Render from State
- 🧨 Taking This Further
🥅 The Goal
We’ll be connecting our React frontend and Express backend to create a full-stack web app. This will allow us to dynamically fetch our entire Oasis roster and render all member’s profile cards on the frontend.
Here’s an example of what this would look like once complete with 2 members on Oasis’ roster:
📦 Setup
To get started, it’s recommended to move both your frontend Profile Generator and backend Oasis Roster API directories into a new parent directory (oasis-starter
in the below example). These should be the only contents in this directory:
oasis-starter/
├─ backend/
│ ├─ ... all backend files ...
├─ frontend/
│ ├─ ... all frontend files ...
Now, open up this new parent directory in VS Code and the terminal. All of your files—both frontend and backend—will now be under one roof. This strategy is referred to as a monorepo (short for “single repository”).
We’ll mainly be focusing on enhancing our frontend to connect to the backend. cd
into your frontend
directory within the terminal.
npm start
within your frontend directory. Similarly, your backend can be started by running npm start
in your backend directory.
By default, the frontend will hot reload changes automatically while the backend will require restarts.🎞️ Creating a Gallery
Let’s start by building a new component to render our roster as a gallery. Create a new file within your frontend/src
directory named Gallery.js
. For now, we’ll use our Output
React component to render a few (static) Oasis members:
// src/Gallery.js
import Output from "./Output";
export default function Gallery() {
return (
<div> {/* Wrapper element to contain all of Oasis members */}
<Output
photo="https://course.ccs.neu.edu/cs2500/_custom/img/assistant/andersonf.jpg"
name="Frank Anderson"
title="Program Coordinator"
fact="Is neither totally left handed nor totally right handed."
/>
<Output
photo="https://course.ccs.neu.edu/cs2500/_custom/img/assistant/_jsella.jpeg"
name="Jay Sella"
title="Mentor"
fact="I listened to over 2,500 artists across 62 genres last year."
/>
{/* As many others as you'd like! */}
</div>
);
}
Even though we’ve created this, it won’t actually show up anywhere on our browser yet. That’s because we are not rendering the new Gallery
component anywhere. We can do this by updating App.js
:
// src/App.js
// other imports ...
import Gallery from "./Gallery";
export default function App() {
// state variables ...
return (
<div>
{/* Header, Form, and Output components ... */}
<Gallery />
</div>
);
}
🔌 Connecting the Backend
While we do now have a gallery, it’s data is static and not being pulled from our backend roster. Let’s change that!
Install Axios
From your frontend directory in your terminal, install Axios with NPM: npm install axios
. This is a popular HTTP client which provides a helpful framework for writing HTTP requests. Official Axios documentation:
GET All Members
With that ready to go, you can now start writing the actual HTTP request. In Gallery.js
, create a new getMembers()
function to fetch all members from our backend roster:
function getMembers() {
axios
.get("http://localhost:8000/members") // GET the members from this endpoint
.then(({ data }) => {
// do this if successful ...
})
.catch((err) => {
// do this if there's an error ...
});
}
.then()
will only run if the HTTP request was successful. Similarly, logic inside the .catch()
block will only run if the request failed (eg, an invalid URL).Next, we need a way to call this function. It should only run the first time our Gallery
component is rendered. We can do this by using the useEffect
hook from React:
useEffect(() => {
getMembers(); // fetch all members
}, []); // only fetch on first render
data
to your browser’s console within the .then()
block: console.log(data)
. For more about the console and how to access it, see Before we can render this member roster, we need to store it. We’ll use React’s useState
hook, similar to how form field values and the showOutput
boolean are handled in App.js
. A single state variable containing an array of all members will suffice for our use case:
const [members, setMembers] = useState([]); // initialize `members` state to an empty array
Putting these pieces together, you can fetch the roster as soon as the Gallery
component is rendered and store the results in your members
state array:
// src/Gallery.js
import axios from "axios"; // import Axios
import { useEffect, useState } from "react"; // import useEffect and useState
import Output from "./Output";
export default function Gallery() {
const [members, setMembers] = useState([]); // initialize `members` state array
function getMembers() { // define getMembers() function
axios
.get("http://localhost:8000/members") // fetch the data
.then(({ data }) => {
setMembers(data); // store the members in state if successful
})
.catch((err) => {
console.error(err); // log an error to the browser console if there's an error
});
}
useEffect(() => {
getMembers(); // get the members the first time this component renders
}, []);
return (
<div>
{/* JSX elements to render */}
</div>
);
}
This is now almost complete! The only piece left is to replace the static profiles with those fetched and stored in state.
Render from State
Within the return
block of your Gallery
component, and inside the <div>
HTML tag, iterate through each element of your members
state array and render the appropriate Output
for each of them:
// src/Gallery.js
// imports ...
export default function Gallery() {
// state, getMembers(), useEffect() ...
return (
<div>
{members.map((m) => (
<Output
key={m.id}
photo={m.photo}
name={m.name}
title={m.title}
fact={m.funFact}
/>
))}
</div>
);
}
key
property come from? React provides this out-of-the-box for all JSX elements. In fact, all JSX elements directly inside a map()
always need keys! These keys tell React which array item each component corresponds to for matching later. This is important if array items can move (eg, sorting), be inserted, or be deleted. A unique key
helps React infer exactly what occurred and make the DOM tree correctly.
Here, we are using the unique id
from each of our members returned from the API.
More on the DOM tree: 🧨 Taking This Further
You can improve the user experience and robustness of this component in a few ways. Here are some ideas based on what you’ve learned so far:
- style the roster
- show a message while the data is loading
- surface any errors to the user rather than just sending them to the browser console (most people don’t look at this!)
- display a custom message if the roster is empty
- create a few buttons that allow the user to sort members (eg, by name or title)
- build out user-specific profile pages (using the second GET API endpoint from your backend)
- add members to the roster when the Profile Generator form is submitted (after hooking up a database to the backend and creating a POST route)
- anything else!