This is a multipart series about building a Puppy Adoption Site with JavaScript. If you haven’t read all the articles please find them here: Episode 1| Episode 2 | Episode 3 | Episode 4 | Episode 5-Part 1 | Episode 5-Part 2 | Episode 6

Let’s talk Frontend: Part 1 Decisions and Setup

Hello everyone! After a summer hiatus, we are back and ready to finish our frontend. In order to do so, we need to discuss a few decisions I’ve made regarding how that’s going to happen.

To begin, we’re going to use a Javascript framework to help structure our frontend. I’ve decided to use ReactJS for our project because ontop of performance benefits, it’ll give us an easy way to compartmentalize components and create a unidirectional data flow.

If you’ve never heard of React before that’s totally cool! At it’s most basic it is, “A JavaScript library for building user interfaces”. If it’s new to you I highly recommend looking into the basics that can be found here: Documentation, Tyler McGinnis, FreeCodeCamp.

For the remainder of this article, I’ll talk about getting set up with our React App, how we’ll use basic User Flows to inform our components and pages, and then how to get some dog data rendering on our app!

So let’s dive in and get the party started!

Step 1: Setting up our React Repo and Application

Like our backend, we’re going to need to create a separate Github Repository to host our frontend application. If you need more clarification on how to get up and running with a Github repository please reference the section Github Repository from Episode 1. (My GitHub repo for the frontend can be found here: https://github.com/mattbasile/Doggo_Land_FE_Tutorial)

This time around make sure to give the repo a different name than your backend, something like Doggo_Land_Client or Doggo_Land_Frontend would be great. Now that we have our empty repository built on GH, go ahead and clone it locally and we’ll get to adding some React!

Step 2: Create React App

Did you know that to get up and running with a React application we can use one line in our terminal? We may need to tweak it for our needs, but Facebook provides a package that will provide us a fully scaffolded React App in seconds.

To implement this, hop in your terminal and cd into your newly cloned repository. Once there run the command npx create-react-app client. Using npx allows us to download packages without having to install yarn or npm locally to our app. When completed you should see a folder appear named client (pictured below)!

What our folder structure should look like after running Create-React-App

Now we’re rolling, inside that client folder is our brand new react app! Let’s start by opening our client folder… Holy Moly that’s a lot of content there. While all these files and folders play an important role in the React ecosystem, the folder we’ll be spending the most time in is our src folder.

Go ahead and open the src folder and see everything at our disposal. Wow, everything in src feels like a lot but don’t fret, fortunately, we can dispose of some of the items in src.

Go ahead and delete logo.svg, serviceWorker.js, App.test.js. Both the serviceWorker and App.test files server more advanced purposes that won’t be required for our app. And the logo.svg is used to provide initial content for our App.

With those removed from our directory, we’ll need to eliminate where we call them in our App, otherwise React with start throwing errors at us because they won’t be able to find those files. Currently, serviceWorker can be found in our index file, so openindex.js and delete lines 5 and 9–12. Though if you’re curious to learn more about what our serviceWorker file can do, feel free to follow the link in the comments in lines 9–12. We also import and reference logo.svg in our App.js file. To remove that openApp.js and delete lines 2 and 9.

Wonderful! Our folders and files are looking clean and ready for action. Let’s see if we can launch this app? Enter your terminal and run the command, yarn start, and our new React Application should be up and running onlocalhost! If it doesn’t work, double-check that you’re running the command from the client folder.

Something like this should render on your screen!

Wahoo! We’re up and running with React. However, it might not be clear what is happening or why this even works this way. The quick answer is our index.js file is using the ReactDOM to render all the items in the <App/> components, into the virtual DOM.

The virtual DOM is different from the traditional DOM in that it’s a lightweight copy that React can manipulate without altering the “true” DOM. Bartosz Krajka said it best in 2015, “Perhaps it’s better to think of the virtual DOM as React’s local and simplified copy of the HTML DOM.” The important structure to understand is that by importing components into our root component of App.js, ourindex.js will render it to our virtual DOM.

That said if we look in our App.js file you’ll notice our App is really just one big function. Which at first can look kind of gnarly, but this is the pattern we use in React. The pattern allows us to create functional and class components that can store our application’s state(data), while rendering out HTML using React’s XML-like syntax called JSX.

When you start adding up all the pieces we have dynamic components that are then passed to our App component. Our App component then manages when and which components are rendered. Which are passed to index.js where this is all rendered in the virtual DOM via our index file.

A lot of moving pieces but our big takeaway for this article and project should be that React will allow us to scaffold our pages and the components that make them up as separate .js files.

That said, let’s start thinking about components and how we want to scaffold them in our project!

Step 3: Components, informed by User Flows

This is a wonderful stage of development we’re at! We have an endless canvas to create an application that looks exactly what we feel like! Right?

Thinking in User Flows

Well… we could slap some mockups together and say hey that looks pretty good let’s make it! But this would be a disservice to our future users. In order to create a successful application, we need to start thinking like a user. And fortunately, for our application, we’ve identified two main users: site visitors and kennel admins.

Designing via User Flows means that we want to identify our top business needs and our top user needs and create a UI and UX that caters to these actions. By identifying the information our users’ need we can start to create pages and component that facilitate the displaying of that information.

Given the scope of our project, I’m about to present a very basic user flow diagram, in fact it’s not even close to a traditional user flow diagram. However, this exercise will provide some context for how to start thinking in user flows. Creating user flows is a real art and valuable skill, to gain a deeper understanding I highly recommend checking out these articles: UXPin & Prototypr.

Let’s map out what a basic visit might look like for a site visitor may look like:

  • Navigate to our URL
  • Hope to see cute photos of puppies they can adopt
  • Filter the cute photos of puppies so they only see labradoodle puppies in Nashville, Tennessee.
  • They find the puppy of their dreams and learn everything about that pooch!
  • They immediately try to contact the kennel to adopt.
  • They love this dog but how reputable is the kennel? They want to learn more about it.
  • Now that they’ve been informed on both they’re ready to contact the kennel.
  • They input their email and name and send a message to the kennel.

Not too crazy! Our core takeaway from these steps should be understanding what pages we should create and what information needs to be provided to our users.

If we first think about information, our site visitors need to see two main things:

  • Dogs: Vital information about a dog’s name, image, age, breed, size, temperament, vaccines, and gender. How do they adopt this dog? Start thinking about how we might visualize this information 🤔.
  • Kennels: Where they are located, what dogs live there, the best way to contact them. Perhaps a v1.01 release but Yelp reviews, or star rating?

That’s pretty much it, right? Our site visitors need to be able to access each of our dogs and then verify the kennel’s information. Because of this, I’m imagining a landing page that contains dog cards that will inform our users about that dog’s specific information, this individual dog card may navigate to a dog page where a user can access a dog’s bio and more information. Also, a user might not be interested in a specific dog, but rather a kennel that’s near them, so perhaps we can mimic the same design pattern for our kennels as well. Also, our users should be able to filter this information! An important thing to consider when trying to optimize our data and UX.

Now that we have a more solidified idea of needs for our users, let’s make a list of web pages and components that are needed:

  • Landing Page: NavBar, Header(explaining our site), Cards Section w/ Dog Cards & Kennel Cards, Toggle Button for Dogs & Kennels. Bonus: A filter so we can distinguish what type of dogs or what region our kennel is in.
  • Dog Page: Dog Image, Expanded information section, Kennel Contact information.
  • Contact Form: A way for users and kennels to connect, and fortunately, we have the database structure to support it(Notifications).
  • Kennel Page: List of dogs available(perhaps we can reuse our dog cards?), Expanded Information section, Kennel Contact information.

I think this is a great start to understanding what our site visitors are going to need and how that will generate pages and components.

Now we need to do the same for admins. Let’s start by mapping out their basic flow:

  • Admin navigates to our URL.
  • They need to login to view their messages
  • They navigate to a login window and successfully logs in with their email address and password.
  • They navigate to their dashboard where they see their dog inventory and available messages.
  • There look through messages and receive the email addresses of interested adopters.
  • They can Add, Delete or Update their dogs in their database.
  • They can update their kennel information to match any changes they’ve made.

Conveniently, an admin’s needs are more structure which allows us to map a user flow more easily. However, that’s not to say in testing we couldn’t discover we totally missed something, but for the time being, we have some great consistent features to provide them.

Let’s map out some pages and components for our admins:

Login Page: Navigation, Login Form, Login Animation SVGs, Registration. Is this accessible from the home screen or is this a hidden URL?

Admin Dashboard: Once logged in an Admin has access to their dashboard. This will contain an updated inventory of their dogs and recent messages. Within their dog inventory, they can add, remove or edit dogs.

Admin Profile: On the dashboard, there will be a link to a Profile page where an admin can update their personal information and kennel information.

Logout Button: Not a full page but a big feature. Once a user logins they should have the option to log out too! I believe our solution will be to create a custom navigation bar for our admin page which allows us to traverse from the profile to the dashboard, to log out.

Ok, now that we have all these great ideas generated, let’s finalize a list of pages and components and then we’ll scaffold them into our React project!

First List of Pages and Components:

I only say first here because there’s always a chance we add or tweak a component as we move through the creation process!

View in more detail here: https://docs.google.com/spreadsheets/d/1lBFVtyhOn0aa2P8ZeWsewVPKxIZBwWTSaR3TGUf5x6g/edit?usp=sharing

Above, we can see a general layout of how we’re going to want to layout our pages and our components. With this at our disposal, let’s recreate this structure within our repository.

Enter our src folder and add two folders named Components and Pages. That’s where we’ll store everything we’ve mapped out above.

In our Pages folder go ahead and add these 6 pages:

  • AdminDashPage.js
  • AdminProfilePage.js
  • DogPage.js
  • KennelPage.js
  • LandingPage.js
  • LoginPage.js

Within each page file add this code chunk:

import React from 'react'export default function FileName() {
return (
<div> </div>
)
}

What does this code mean? Similar to our App.js and index.js files we’re providing organizational space to render page-specific components. Later, we’ll host all of these in our App.js file so that we can switch from view to view depending on what page we have selected. It’s important to note that the variable FileName used above reflects the current file we’ve just added the code to. For example, we’d replace that with DogPage or KennelPage depending on the file.

Now in our components folder let’s first start by adding 3 new folders:

  • Utilities
  • Admin
  • Users

Utilities will host our generic components that either user type can use, Admin will host admin specific components, and you guessed it, Users will host user-specific components.

Before we add anything to our sub-component folders, let’s start drafting a more thorough mockup and decide what components we’ll need from there.

Plot Twist! We’re going to save mockups for next episode. Instead, we’ll wrap up this article with the reason we all came to this series, to get a frontend communicating with the backend.

For the convenience of our site, every time someone visits we’ll need to collect all of our the dog information from our backend. To do this, we’ll implement a Hook in our App.js file that will process an HTTP request and then store the results in our state!

React Hooks were released on February 16th, 2019 and are a function that allows us to “ ‘hook into’ React state and lifecycle features from function components”. This will allow us to code our React App without relying on a class component to manage state. This is a little confusing if you are new to React but ultimately it provides an improved way for us to share logic across components. Additional benefits can be found in this article.

However, we’re not so worried about the history of Hooks, we just want to grab some dog data from our backend! To do so, let’s navigate to our App.js file, and start creating our first hook.

In order to do this, we’re going to need to start by prepping our environment. First let’s import two things from React, useEffect and useState. These two functions will allow us to manage state as well as create lifecycle hooks such as componentDidMount so that we can make sure we execute our HTTP request every time our App is rendered. To use them we must import them from the React API, so on line 1 where we import React, replace it with this line: import React, {useEffect, useState} from 'react';

With that off our list, we just need to import a module that will help us create a connection to our backend. We could rely on the native Fetch API, but I prefer using a module called Axios. You can learn more about it here, but I enjoy it because it’s specifically tailored to handle Node.js. To add Axios to our project hop in your terminal, make sure you’re in the client directory, and run the command yarn add axios. Then to add it to our App.js file, go to line 3 and add the line import axios from 'axios';.

Awesome, we’re now ready to add some code! I’m going to first start off by giving you the entire code chunk and then breaking it down piece by piece. But before I paste it, we need to start up our frontend and backend! Hop into both the frontend client directory and just our backend root directory and run the commands yarn start. This should make your frontend run on port 3000 while your backend runs on port 5000.

With that squared away, here is our App function’s code.

function App() {
const [dogs, setDogs] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(()=>{
axios.get("http://localhost:5000/api/visitors/dogs")
.then(res=>{
setDogs(res.data)
setIsLoading(false)
})
.catch(err=>console.log(err))
})
return (
<div className="App">
{isLoading ?
<h2>Gathering the best Doggos in the world!</h2>
:
dogs.map(dog=>{
return <p>{dog.name}</p>
})
}
</div>
);
}

***EDIT TO OUR DB: Above you’ll see we try and access dog.name but I realized in Episode 4 our tables did not include a name field for our dog's table. Meaning our best friends don’t have any names! What we need to do is rollback our DB and repeat the final seeding steps with our new code in place. Step 1: run knex migrate:rollback this should rollback most recent db changes. Step 2: dog’s migration file add this line of code tbl.string(“name”, 250); and in our dog’s seed file add this line to each seed “name”: faker.name.firstName(). Step 3: Once those files are saved run knex migrate:latest followed by knex seed:run and you should be all caught up! Sorry for the confusion***

When we look over this code, it’s only 22 lines, but there’s a lot going on. Let’s dive in:

useState( ):

const [dogs, setDogs] = useState([]);
const [isLoading, setIsLoading] = useState(true);

Our first two lines are used to define the state we need to load and store our dog data. The first array, [dogs, setDogs], represents the current state and a function that let’s us update it. For our usuage, dogs will act as the state variable we can store the dog data we retrieve from the backend too and setDogs will act as the function that allows us to update dogs. To begin, we initialize our state with an empty array and the syntax, useState([]), which basically says those values defined before me are stateful and whatever you pass to me will be their initial state. Now that we initialize our dog state with an empty array when we retrieve dog data from our backend we can just replace it!

Similarly, we follow the same pattern with isLoading so that we can monitor and reflect the progress of our API calls in our UI. However, notice we pass a boolean to useState()? By using a boolean we can later toggle the Loading state which will allow us to implement a ternary operator in our render function. Where depending on what the value of isLoading is it can dynamically display a message or our loaded dog info. Integrating a loading state allows us to create a more pleasant experience for our users and keeps them more aware as to what’s going on. If you want to learn more about using useState() please take a look at the following article.

useEffect( ):

useEffect(()=>{
axios.get("http://localhost:5000/api/visitors/dogs")
.then(res=>{
setDogs(res.data)
setIsLoading(false)
})
.catch(err=>console.log(err))
},[])

This might look like a bunch of mumbo jumbo, but our experience building out the backend has prepared us to break down this syntax.

useEffect, allows us to perform “side effects” whenever parameter we pass in gets altered. In our usage, we pass an empty [] to our useEffect call. This tells useEffect that any time our component renders, very similar to the former componentDidMount lifecycle method. Inside our useEffect we’re implementing axios to make an HTTP GET request to our localhost. We then use a then catch block to process the data we receive back. If we get positive information we’ll enter our then block where we’re passing in the response(shortened as res) and we use it to edit our state. My suggestion to you is to hop into our then block and runconsole.log(res) take a look at all the information coming back from our server. When we deal with APIs in the wild, understanding all the information we’re getting back is important because it allows us to locate the nuggets that are important to our success. In this case, we locate res.data and see our dogs array! Then using the function setDogs we update our dogs state to reflect our collected data. Once that’s squared away, it is safe to say we’re done loading so we can toggle our loading state to false via our setIsLoading function. Lastly, our catch block is there in case this doesn’t go so smoothly. And for our purposes, we’ll just console.log() that error but in the future, it would be nice to create an error message so our users are left wondering what happened!

return( ):

return (
<div className="App">
{isLoading?
<h2>Gathering the best Doggos in the world!</h2>
:
dogs.map(dog=>{
return <p key={dog.id}>{dog.name}</p>
})
}
</div>
);
}

Lastly, we need to populate some content on our page! The quickest way to do that is by writing in our App.js return statement. In React, whatever comes in a component’s return statement is what’s rendered as part of the virtual DOM aka what we see on the screen. In our case, we’re using a ternary operator to dynamically render content depending on what state our application is in. If is loading is true, we’ll alert our users that we’re searching for dogs. Otherwise, we’ll be taking our populated dogs and return a p tag with their name. Since our dogs data stored in state is an array, we can map over the values and because we’re interested in rendering this info we can return each dog in the array as a <p> tag. We then can access our the current dog’s id and name to make each <p> unique and display some valuable content.

Conclusions

There we have it, folks! We finally have our frontend and backend chatting and getting along. Next, we need to create an enjoyable experience for our users. The next episode we’ll take steps towards achieving this by designing some mock-ups and coding out the necessary components.

Stay tuned and don’t hesitate to reach out if you have any questions! Happy Coding! PS: Here’s the repo I’m using to build out the frontend as well — https://github.com/mattbasile/Doggo_Land_FE_Tutorial

--

--

Matt Basile
Matt’s Lambda Minutes

Full Stack Developer sharing updates of his journey through Lambda School’s course. Check-in for a recap of core concepts and how-to guides.