Starting The Front End
Section 2.4: React Setup & Git Initialize
Generate a boilerplate of a react application, including the library
Create new folder for project
Open with VSCode
npx create-react-app frontendThis is the client, the front end, the UI part of our application
The backend folder will be Node, Express, database, etc.
cdinto the frontend folder, then runnpm startto run our frontend dev-serverOn localhost:3000 by default
You’ll notice the package.json has all the dependencies that we use
(i.e. - react-dom is the library used for react to work with the document)
(i.e. - react-scripts includes the dev server that we’re running and stuff)
NPM scripts such as
npm run build, which build out all of our static assetsWe can also run tests
Navigate to
index.htmland observe that React is a single page application frameworkIt is used to create single page applications
The browser initially reads this single
index.htmlNavigate to the src folder and into
index.js, which is the entry pointIt brings in React and React-DOM
React-DOM has a method called Render, which takes our main app component from
App.jsThe Render places this component into an
idvia vanilla JavaScript thatindex.htmlpicks up**Please Note: ** every custom component that we create will be put into
App.jsNavigate back to the
index.htmland remove the commentsChange the
<title>tag in the<head>tagLater in the course, we use react-helmet, which allows us to have custom page titles
Change the
<meta>description to something that makes more senseUpdate the
favicon.icofile so that you no longer default to the React logoDelete the following unnecessary files:
App.css,App.test.js,logo.svg, andsetupTest.jsNavigate into App.js (where you can import images, svg, css, etc.) to remove above
importsIn this tutorial we will be using function based components with hooks
Notice that we will be using
jsx, which is similar to HTML butclassNameis used as an attribute becauseclassis reserved**Please Note: ** You can have JavaScript expressions within the curly braces of
jsx.(i.e. variable, conditional, ternary)
Remove everything within the
Appcomponent besides the<div className="App"/>Note that when you
returnoutput inside of a component, everything has to be wrapped in a single element (it doesn’t have to be a<div>)Note that a fragment is an empty element
Delete everything inside of the
index.cssChange the
functionApp syntax toconst App = () => { return ( ... ) }Using Git for version control is recommended for this course
Note that we will later on be deploying from GitHub to Heroku anyway
Consider
ls -athenrm -rf .gitto delete this git repositoryWe want it in the root of the project and not the frontend folder
Move the
.gitignorefile into the rootWhat this file does is allows you to describe any files/folders you don’t want pushed to your repo (i.e.
node_modules)Add
node_modules/because we’ll have one in our root for all of our server dependenciesWe will also have one in our frontend (make sure to exclude both of these)
Ignore
.env, as the file will be all of our environment variables; our global variablesWe definitely don’t want to contain sensitive information like API keys, MongoDB URI, etc.
cd ..out of the frontend folderEnter
git initin the console to initialize our repository (note we deleted one earlier)git add .>git statusto see our staginggit commit -m 'comment'Make commits along the way
cdback into the frontend folder then runnpm start.In the next video, we create
<Header/>, which also will have a navbar, and<Footer/>, as well as some other things
Section 2.6: HomeScreen Product Listing
Set up
HomeScreen.jsand list out all of our productsWe will use a JavaScript file with a products array with some dummy data
Later on we wil be actually fetching products from our backend and ultimately from a db
For now though we should have in our project files an
images/folder and aproducts.jsfileCopy the
images/folder into thepublic/folder (they are the product photos)Bring the
products.jsfile into thesrc/folderObserve that products.js is an array of objects, with key values of string and number
One of the important notes about the
image:key is that its value is to theimage/folderIn our
src/folder create a new folder calledscreens/>HomeScreen.jsrafcethen tab in the fileimport products from '../products'What we want to do here is loop through all the products and then output
Each one will be its own product component, which we create in a little while
Replace the component’s
<div>with an empty fragment<> </>Add in a Bootstrap
<Row>andimport { Row, Col } from 'react-bootstrap'Inside the
<Row>loop through our products that we imported abovei.e. like
{products.map(product => (<Col sm={12} ><h3>{product.name}</h3></Col>))}Go to our
App.jsthenimport HomeScreen from './screens/HomeScreen'Within the
<Container>add<HomeScreen />then review the page againThis way we are able to bring the products into the file and loop over them
Go back to the HomeScreen code and replace the
<h3>``s with ``<Product />Remember that Components can take in
propsIn this case we will be accessing
product={product}in<Product>passed from aboveimport Product from '../components/Product'Navigate to
src/components/and createProduct.jsthen use therafceshortcutAdd in a
<Card>and make sure this isimportedfromreact-bootstrapWhen we implement
react-routerwe will use the<Link>tag but for now juse use<a>Within the
<Card>add<a href={`/product/${product._id}`}>Within that
<a>Add in<Card.Img src={product.image} variant='top' />Next, within the
Productfunctional component, destructure({product})as a propYou should now see columns of images on the main page
Go back to the
Productand below the link with the image and add<Card.Body>Paste in the
<a>code from above but change<Card.Img>to<Card.Title as='div'>Within the
Card.Titleadd<strong>{product.name}</strong>Before the ending of the
</Card.Body>add<Card.Text as='div'>then a<div>Within this
<div>add{product.rating} from {product.numReviews} reviewsBelow the
</Card.Text>add another<Card.Text as='h3'>and add${product.price}In the next video, we will be adding the
<Rating>component
Section 2.7: Rating Component
We will create the
<Rating>component to make stars appear along with Font Awesome icons.import Rating from './Rating'and remove the{product.rating} from {product.numReviews} reviews<div>Here
<Rating/>will take in 2 props:value={product.rating}andtext={`${product.numReviews} reviews`}Go to the
components/folder and create aRating.jsfile, then use therafcesyntaxRatingarrow function component will take the 3 props{ value, text, color }Create
<span>within to represent each one (?)Head to FontAwesome.com to choose the stars you want to use
Which star we show is going to depend on the value that’s passed in, which is the product rating
The conditional code in
Rating.jswill always show 5<span>tags nested with FontAwesome<i>icons, however thevalueattribute/prop will determine whether the star is filled, half, or emptyWe also use a span for
{text && text}to short-circuit evaluateIn React we can add inline styles, but we need to use double curly braces
style={{color}}For example we could add
color='red'but here we will want to have a default yellowFor a default Prop value, in the bottom of
Rating.jswe can setdefaultPropsobjectIn
Rating.jsaddimport PropTypes from 'prop-types'to specify the type of each prop we’re using (this is optional)This will now type-check our props
Beware the warning
Warning: Each child in a list should have a unique "key" prop.What that means is that when we create a list such as we did in
HomeScreen.jsthe element has to have akeyand that needs to be uniqueSo, back on
HomeScreen.js, make each looped<Col key={product._id}>and that warning should go awayNavigate to
index.cssto add.rating span { margin: 0.1rem }At this point you should be able to see both our ratings and our number of reviews
In the next video, Traversy will start to implement the routing
We will be able to click a link and go to a separate
ProductScreenSearch for the React Web Tools from chrome web store
Search for the Redux Dev Tools and install
Now in the console, with the React tools we can see the tree of Components, including the Bootstrap components
This will also show you the props that are passed into each Component
In the next video, we set up React Router
React Router v6 has been released and has some changes, so see the video below to begin
Section 2.8: Note on React Router
Review Traversy’s video on React Router v6 changes
React Router v6 breaks a lot of things from v5 (outdated)
As an aside,
npm i react-router-dom@latestupdates to the latest version for npm packagesOne error to resolve is
Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>The
<Switch>element does not exist anymore - it’s been switched to<Routes>Now you have to wrap your
<Route>``s in ``<Routes>(seeApp.js) for an exampleimport { BrowserRouter as Router, Routes, Route } from 'react-router-dom'The
<Route>components now have anelementattribute that that takes in JSXexactis no longer needed for routesAlso note the error
Error: [h3] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>When adding a
<Route>, make sure toimportthe component! (see:App.js)Params are still dealt with similarly as did v5, i.e.
path='/task/:id'<Link to={`/task/${task.id}`>syntax remains the same as from v5Another import of note is the
userParamsfunctionality fromreact-router-domThe above is how we get params (i.e. id’s); these can also be destructured
You can test this object out by
const params = useParams(), thenconsole.log(params)The above replaces the destructuring of such object items as
match, because these props are not available anymore“Redirect” has also been replaced with
Navigateinreact-router-domi.e.
if(error){return <Navigate to='/'> />}There is also a
useNavigatehook! For more information on hooks, see React documentationFirst do
const navigate = useNavigate(), then for exampleif(res.status === 404){navigate('/')}navigate(-1)will take us to the last pageuseLocation()is another hook available inreact-router-domfor location data such aspathnameAgain, this could be destructured
const { pathname } = useLocation()To recap, no more <Switch>, you have to use <Routes>, new attribute with JSX, new hooks
For more info on React Router Hooks: Using Hooks: React Router
Section 2.9: Implementing React Router
In this video, we implement the React Router so that we can have different routes, different URLs that we can go to in our project in our front end
First off, make sure you’re in the
frontend/folder and we’re going tonpm i react-router-dom react-router-bootstrapWe also want to install the
react-router-bootstrappackage, because when we’re dealing with links in the navbar or buttons and stuff, we want to use thatGo to
App.jsto implement the<Router>(see Section 2.8, Step 7)BrowserRouteruses the HTML5 History API, i.e. push state and replace state, and also hash routerWe can alias the above with the following syntax:
import {BrowserRouter as Router} from react-router-domWe needed to wrap our entire App with the
<Router>in order to use it (return ())Whenever we want to create a
<Route>, we provide apathand what component we want to load when we go to that pathAs an example:
<Route path='/' element={<HomeScreen />} />Navigate to the
screens/folder in VSCode and create a new file:ProductScreen.jsGenerate a component
In
App.js, bring that screen in (ProductScreen)Create a route for that, but with the path to be:
path='/product/:id'The
/:idis a placeholder, and that’s going to be looked at as theidparamMake the
elementequivalent to the<ProductScreen />componentReviewing the
Project.jsfile, you can see how the<Link>usesproject._idNext we fix the
<Header>links so that we maintain a single-page applicationNavigate to
components/Header.jsandimport { LinkContainer } from 'react-router-bootstrap'Observe that in
Product.jsweimport { Link } from 'react-router-dom'Replacing
<a>tags with<Link>tags removes the reloading of the pages and is much faster<LinkContainer>fromreact-bootstrapallows us to wrap bootstrap components<LinkContainer>can be used multiple times within the same componentIn the next video, we will implement the
<ProductScreen>In the beginning, we will just take from the
product.jsfile from the arrayUltimately, this data will come from the backend (when seeded), but we use this in the beginning just to get our data showing
Note: This strategy should be utilized in projects moving forward (i.e. Reactionary)
Section 2.10: Product Details Screen
Now we start on the Product page itself, or
<ProductScreen/>Navigate to
ProductScreen.jsandimport { Link, useParams, useNavigate } from 'react-router-dom'Also make sure to
import { Row, Col, Image, ListGroup, Card, Button, Form } from 'react-bootstrap'We also use the
<Rating/>component, so import that too (from '../components/Rating')Also make sure to bring in our
productsfrom'../products'Since products is just an array of objects, we can use the higher order array method
find()For each product, where the product._id === match.params.id – this is code we end up not using
Now in the ProductScreen, we can dynamically display product.names if we want to
Use
<Link className='btn btn-dark my-3' to='/'>around text to create a Back buttonWithin
<Row><Col>, add areact-bootstrap <Image>componentWithin a separate
<Col>nested within the same<Row>, add a<ListGroup>with accompanying ``<ListGroup.Item>``sTo learn more about react-boostrap components, view their documentation here
In the next
<ListGroup.Item>, nest within it the<Rating>component, which as you’ll recall can take some props such asvalueandtextReview the
ProductScreen.jsfor more examples of product data being added to LGIsWithin a third
<Col>, create areact-bootstrap <Card>componentInside here, observe the code for single LGS with one
<Row>of 2 columns in eachAlso observe the
product.countInStockJSX that uses ternary operators for dynamic textFor now, there is also an “Add to cart”
react-bootstrap <Button>Note that there is good use for this in future projects such as
Save CommentbtnsAlso note the
disabled<Button> attribute for when the count in stock is 0Observe here that a <Button> might be disabled when a User isn’t logged in, or simply redirects to a sign-in page when the User isn’t logged in
Note that eventually, the
userInfostate data can determine if one message appears, so perhaps if the user is not “Signed In”, one could remove the button or disable itAs an aside, note that in the
product.jsfile (in thesrc/) there is actually a_idkeyIn future videos, we will have a quantity feature on this page, but not until cart is set up
In the next section, we begin working on the
backend/Brad creates a simple
expressserver that will serve the products from the backend, as opposed to just the current JavaScript file being used on thefrontend/We are going from the file on the frontend to the backend, where it will be served up
After that, we will serve it from a MongoDB database
An important note here: when I work on my Reddit Reactionary App, I’ll also want to start off by serving test data via the frontend, then the backend, then finally from the database once data is there.