Course: Section

Get Started with Modern React: Step by Step

Episode: Title

S02・V25: Thinking in React (Part 3)

Date Created: July 17th, 2019
Last Updated: 27 days ago

Objectives
  1. We will build a static version of “ProductTable”.
  2. We will build a static version of “ProductRow”.
  3. We will build a static version of “ProductCategoryRow”.
Watch Video
Duration: 8m 12s

Static ‘ProductTable’

In the previous video, we added a placeholder for the ProductTable.

We will now add the ProductTable element to the FilterableProductTable component. We will pass the products data to the ProductTable as a prop, which it needs for display.

const FilterableProductTable = props => {
  const { products } = props;

  return (
    <div style={{ fontFamily: "sans-serif" }}>
      <SearchBar />
      <ProductTable products={products} />    </div>
  );
};

We will continue by defining the ProductTable component.

First, we will destructure products from props. Next, we will return a full-width table. We will add thead and tbody elements to the table. The thead will have a tr, with blue-colored contents. We will add two ths to the thead.

const ProductTable = props => {
  const { products } = props;

  return (
    <table width="100%">
      <thead>
        <tr style={{ color: "blue" }}>
          <th align="left">Name</th><th align="right">Price</th>
        </tr>
      </thead>
      <tbody>
      </tbody>
    </table>
  );
};

In the tbody, we want to show the filtered list of product names and prices. Each row of the filtered list will be a ProductRow element.

We will start by defining a variable called rows, as an array of ProductRow elements to display. Initially, the rows array is empty. We will push each ProductRow onto the rows array, using the forEach() method. For each product in the products data array, push a ProductRow onto the rows array.

We will inject the product into the ProductRow as a prop, which it will need to show the name and price. In addition, we will need to specify a key to each ProductRow in the rows array. We will use the product name as the key.

Now, we will add the rows array to the tbody.

const ProductTable = props => {
  const { products } = props;
  const rows = [];
  products.forEach(product => {    rows.push(<ProductRow product={product} key={product.name} />);  });
  return (
    <table width="100%">
      <thead>
        <tr style={{ color: "blue" }}>
          <th align="left">Name</th><th align="right">Price</th>
        </tr>
      </thead>
      <tbody>
        {rows}      </tbody>
    </table>
  );
};

Static ‘ProductRow’

Next, we will define the ProductRow component.

First, we will destructure the product from props. Then, we return a tr. Within the tr we will display a td with the product name, and the product price.

const ProductRow = props => {
  const { product } = props;

  return (
    <tr>
      <td>{product.name}</td><td align="right">{product.price}</td>
    </tr>
  );
};

In our mock, the products that are not in stock display their name in red. We will need to add this feature to the ProductRow component.

If the product is stocked, then display the product name as it is now. Otherwise, display it with a red color. Assign the result of this ternary expression to a variable, which we will call coloredName. Finally, we will update the returned td showing the product name.

const ProductRow = props => {
  const { product } = props;
  const coloredName = product.stocked ?    product.name :    <span style={{ color: "red" }}>{product.name}</span>;
  return (
    <tr>
      <td>{coloredName}</td><td align="right">{product.price}</td>
    </tr>
  );
};

We still need to add the ProductCategoryRow to the FilterableProductTable, which we will define shortly.

We will push a ProductCategoryRow element to the rows array in the ProductTable component. We will inject the product into the ProductCategoryRow as a prop, which it will need to get the category. In addition, we will need to specify a key to each ProductCategoryRow in the rows array. We will use the product category as the key.

const ProductTable = props => {
  const { products } = props;
  const rows = [];

  products.forEach(product => {
    rows.push(      <ProductCategoryRow        product={product}        key={product.category}      />    );    rows.push(<ProductRow product={product} key={product.name} />);
  });

  return (
    <table width="100%">
      <thead>
        <tr style={{ color: "blue" }}>
          <th align="left">Name</th><th align="right">Price</th>
        </tr>
      </thead>
      <tbody>
        {rows}
      </tbody>
    </table>
  );
};

Static ‘ProductCategoryRow’

Now, we will define the ProductCategoryRow component.

Next, destructure product from props. Now, return a tr with a th that spans two columns. Within the th, display the product category.

const ProductCategoryRow = props => {
  const { product } = props;

  return (
    <tr>
      <th colSpan="2">
        {product.category}
      </th>
    </tr>
  );
};

There is a bug in our ProductTable.

The ProductCategoryRow shows before each ProductRow, but it should only appear once before a group of products within the same category. The PRODUCTS dataset has products grouped by category, so we can check which category of product was last displayed, and only show the ProductCategoryRow when the category changes.

We will define a variable called lastCategory, and initially set it to null. Now, we will check if the current product’s category differs from the lastCategory, and only push the ProductCategoryRow on to the rows array if that is the case.

Finally, we will update the lastCategory to be the current product’s category after pushing the ProductRow to the rows array.

import React from "react";
import ReactDOM from "react-dom";

const ProductCategoryRow = props => {
  const { product } = props;

  return (
    <tr>
      <th colSpan="2">
        {product.category}
      </th>
    </tr>
  );
};

const ProductRow = props => {
  const { product } = props;
  const coloredName = product.stocked ?
    product.name :
    <span style={{ color: "red" }}>{product.name}</span>;

  return (
    <tr>
      <td>{coloredName}</td><td align="right">{product.price}</td>
    </tr>
  );
};

const ProductTable = props => {
  const { products } = props;
  const rows = [];
  let lastCategory = null;
  products.forEach(product => {
    if (product.category !== lastCategory) {      rows.push(
        <ProductCategoryRow
          product={product}
          key={product.category}
        />
      );
    }    rows.push(<ProductRow product={product} key={product.name} />);
    lastCategory = product.category;  });

  return (
    <table width="100%">
      <thead>
        <tr style={{ color: "blue" }}>
          <th align="left">Name</th><th align="right">Price</th>
        </tr>
      </thead>
      <tbody>
        {rows}
      </tbody>
    </table>
  );
};

const SearchBar = () => {
  return (
    <form>
      <input type="text" placeholder="Search..." />
      <p>
        <input type="checkbox" />
        {" "}
        <span style={{ color: "green", fontSize: "smaller" }}>
          Only show products in stock
        </span>
      </p>
    </form>
  );
};

const FilterableProductTable = props => {
  const { products } = props;

  return (
    <div style={{ fontFamily: "sans-serif" }}>
      <SearchBar />
      <ProductTable products={products} />
    </div>
  );
};

const PRODUCTS = [
  {
    category: "Sporting Goods",
    price: "$49.99",
    stocked: true,
    name: "Football"
  },
  {
    category: "Sporting Goods",
    price: "$9.99",
    stocked: true,
    name: "Baseball"
  },
  {
    category: "Sporting Goods",
    price: "$29.99",
    stocked: false,
    name: "Basketball"
  },
  {
    category: "Electronics",
    price: "$99.99",
    stocked: true,
    name: "iPod Touch"
  },
  {
    category: "Electronics",
    price: "$399.99",
    stocked: false,
    name: "iPhone 5"
  },
  {
    category: "Electronics",
    price: "$199.99",
    stocked: true,
    name: "Nexus 7"
  }
];

ReactDOM.render(
  <FilterableProductTable products={PRODUCTS} />,
  document.getElementById("root")
);

Save the file and view the completed static version of the app. It is very similar to our mock, so we have successfully implemented the static version.

Summary

We built a static version of ProductTable, ProductRow, and ProductCategoryRow.

Next Up…

In the next video, we will continue with Part 4 of “Thinking in React”.