Setting up the SPA in ReactJS

Building a frontend single-page application in ReactJS with pages and components.

Project Goals

In this third stage of the MERN project, you will:

  • Create a single-page app in ReactJS to support multiple routes and install the required npm packages.
  • Add pages and components to the ReactJS app.
  • Display MongoDB product data, via an Express backend routes, in your ReactJS frontend app home page.

Installing the frontend packages

In this stage of your MERN full-stack project, you will set up a single-page application (SPA) on the frontend.

Begin by installing the following packages in your /frontend folder:

npm i react-router-dom axios @mui/material @emotion/react @emotion/styled

Updating your main.jsx file

Here are the steps to add routes for the 'pages' of your SPA.

  1. In your frontend/src subfolder, open the main.jsx file and delete the import index.css statement. screenshot
  2. Add this new import statement:
    import { HashRouter as Router, Routes, Route } from "react-router-dom";
  3. Inside the <React.StrictMode> tag pair, update the <App /> component with the following code:
    <Router>
        <Routes>
                {/* App component wraps around all the 'pages'. */}
                <Route path="*" element={<App />} />
        </Routes>
    </Router>
    App.jsx will now act as a layout component that wraps around all the ‘pages’ in your frontend ReactJS app.

Creating your Home page

Follow these steps to create a component that will be the home page of your frontend:

  1. Inside the frontend/src sub-folder, create a new folder named /pages.
  2. Inside the /pages folder, create a new text file named HomePage.jsx. This file will serve as your home page.
  3. Add the following code to this file:
    import React from "react";
    
    const HomePage = () => {
        return (
            <>
                <h1>My HomePage</h1>
            </>
        );
    };
        
    export default HomePage;
  4. Replace all the content of your frontend/src/App.jsx file with the following:
    
    // Import Route and Routes from the React router library
    import { Route, Routes } from "react-router-dom";
    // Import the HomePage 
    import HomePage from './pages/HomePage';
    
    function App() {
        return (
            <Routes>
                {/* Use Home component for the root path */}
                <Route index element={<HomePage />} /> 
            </Routes>
        );
    }
    
    export default App;

If you run the frontend app now in your browser, it should look similar to that below.

screenshot

Updating your Home page to display data

Follow these steps to display data from MongoDB, through a route in your Express backend app, in your Home page:

  1. Update your HomePage.jsx file as follows:
                    
    import React, { useEffect, useState } from "react";
    import axios from "axios";
    
    const HomePage = () => {
      const [productList, setProductList] = useState([]);
    
      const getProduct = async () => {
        try {
          const response = await axios.get("http://localhost:5000/read");
          setProductList(response.data);
          console.log(response.data);
        } catch (e) {
          console.log(e);
        }
      };
    
      useEffect(() => {
        getProduct();
      }, []);
    
      return (
        <>
          <h1>My HomePage</h1>
          {/* Map over the productList array to display each product */}
          {productList.map((product, index) => (
            <div key={index}>
              <h2>{product.name}</h2>
              <p>{product.description}</p>
            </div>
          ))}
        </>
      );
    };
    
    export default HomePage;

If your Express app is running, your frontend app should now display in your web browser a list of product titles from the MongoDB database. screenshot

Adding a product card component

Follow these steps:

  1. In your frontend/src folder, create a new sub-folder named /components.
  2. Inside this /components sub-folder, create a new text file named ProductCard.jsx.
  3. Paste the following code into this file:
      
    import React, { useState } from "react";
    import Typography from "@mui/material/Typography";
    import Card from "@mui/material/Card";
    import CardHeader from "@mui/material/CardHeader";
    import CardMedia from "@mui/material/CardMedia";
    import CardContent from "@mui/material/CardContent";
    import Rating from "@mui/material/Rating";
    import Stack from "@mui/material/Stack";
                    
    const ProductCard = (props) => {
      // Initialize product state variable with the product prop
      const [product, setProduct] = useState(props.product);
                    
      return (
            <>
                <Card
                    sx={{
                        width: 345,
                        height: 550,
                        display: "flex",
                        justifyContent: "space-between",
                        flexDirection: "column",
                    }}
                >
                    <CardHeader title={product.title} />
                            <CardMedia
                              component="img"
                              height="194"
                              image={product.images}
                              alt="Product image"
                    />
                    <CardContent>
                              <Stack direction="column" spacing={1}>
                                <Typography variant="body2" color="text.secondary">
                                  {product.description}
                                </Typography>
                                <Stack direction="row" spacing={1}>
                                  <Rating
                                    name="half-rating-read"
                                    defaultValue={product.rating}
                                    precision={0.5}
                                    readOnly
                                  />
                                  <Typography variant="body1" color="text.primary">
                                    {product.rating}
                                  </Typography>
                                </Stack>
                                <Stack direction="column">
                                  <Typography variant="body1" color="text.primary">
                                  ${product.price} 
                                  </Typography>
                                  <Typography variant="body1" color="text.primary">
                                    Price discount: {product.discountPercentage}%
                                  </Typography>
                                </Stack>
                              </Stack>
                        </CardContent>
                    </Card>
                </>
            );
        };
                    
    export default ProductCard;
  4. Update your HomePage.jsx page to use this component. screenshot
    return (
      <>
      {/* Render a heading with the text "My HomePage" */}
      <h1>My HomePage</h1>
      {/* Check if productList is not empty */}
      {productList.length !== 0 &&
        // If productList is not empty, map over it and render a ProductCard component for each product
        productList.map((product) => (
          // Render a ProductCard component with a unique key and the product as a prop
          <ProductCard 
            key={product._id}
            product={product}
            getProduct={() => getProduct()} />
        ))}
      </>
      // End of the component's return statement
    );

In your web browser, you should see a single-column list of product details in card format. screenshot

Project checklist and next step

Before continuing:

icon

CHECK that your ReactJS home page can list products using the frontend getProduct() function and the backend http://localhost:5000/read route.

In the next step you will add frontend components and pages to handle CRUD operations with the database.