Get Started with Modern React: Step by Step
S02・V25: Thinking in React (Part 3)
- We will build a static version of “ProductTable”.
- We will build a static version of “ProductRow”.
- We will build a static version of “ProductCategoryRow”.
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 th
s 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”.