Course: Section

Get Started with Modern React: Step by Step

Episode: Title

S02・V27: Thinking in React (Part 5)

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

Objective
  • We will apply state to our application.
Watch Video
Duration: 5m 20s

Apply State to our App

Now, we’ve decided that our state should be in FilterableProductTable.

We’ve determined that the search text the user has entered is state. We will call the state variable filterText, and give it an initial value of an empty string.

We determined that the value of the checkbox, which when checked only shows products in stock, is the other piece of state of our application. We will call the state variable inStockOnly, and give it an initial value of false.

We will need to import useState from the react library. We will now inject our filterText and inStockOnly state, to both the SearchBar, and ProductTable components.

const FilterableProductTable = props => {
  const [filterText, setFilterText] = useState("");  const [inStockOnly, setInStockOnly] = useState(false);  const { products } = props;

  return (
    <div>
      <SearchBar
        filterText={filterText}        inStockOnly={inStockOnly}      />
      <ProductTable
        products={products}
        filterText={filterText}        inStockOnly={inStockOnly}      />
    </div>
  );
};

Now, let’s make use of filterText and inStockOnly within our SearchBar component, via props. We will make our text input field a controlled component using the filterText value. And, we will make our checkbox input a controlled component using the inStockOnly checked value.

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

We can now update the initial values of our state variables in the FilterableProductTable component to see if we can correctly control the SearchBar. We will change the initial value of filterText to be "ball", for example. And, we will change the initial value of inStockOnly to be true.

  const [filterText, setFilterText] = useState("ball");
  const [inStockOnly, setInStockOnly] = useState(true);

Save the changes and check if the input fields in the browser reflect these changes. We can now undo these changes.

  const [filterText, setFilterText] = useState("");
  const [inStockOnly, setInStockOnly] = useState(false);

We will now make use of our state variables in the ProductTable component, via props. We can make use of these prop values when pushing ProductCategoryRows into the product table.

If the product name does not include the filterText the user is searching for, then the product should not be pushed to the product table. We can implement this by checking the indexOf() the filterText in the product name, and if it is -1, then return without pushing.

In addition, if the user has chosen only to see products in stock, and the product is not stocked, then similarly return without pushing.

const ProductTable = props => {
  const { filterText, inStockOnly } = props;  const { products } = props;
  const rows = [];
  let lastCategory = null;

  products.forEach(product => {
    if (product.name.indexOf(filterText) === -1) {      return;    }    if (inStockOnly && !product.stocked) {      return;    }    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>
  );
};

We can now update the initial values of our state variables in the FilterableProductTable component to see if we can correctly control the ProductTable.

As we did before, change the initial value of filterText to be "ball". You can see in the browser that the product table only shows products containing the text “ball”. Let’s undo this change.

And again, we will change the initial value of inStockOnly to be true. You can see in the browser that the product table only shows products that are in stock. Let’s undo this change.

Our app now looks like this:

import React, { useState } 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 { filterText, inStockOnly } = props;
  const { products } = props;
  const rows = [];
  let lastCategory = null;

  products.forEach(product => {
    if (product.name.indexOf(filterText) === -1) {
      return;
    }
    if (inStockOnly && !product.stocked) {
      return;
    }
    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 = props => {
  const { filterText, inStockOnly } = props;

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

const FilterableProductTable = props => {
  const [filterText, setFilterText] = useState("");
  const [inStockOnly, setInStockOnly] = useState(false);
  const { products } = props;

  return (
    <div style={{ fontFamily: "sans-serif" }}>
      <SearchBar
        filterText={filterText}
        inStockOnly={inStockOnly}
      />
      <ProductTable
        products={products}
        filterText={filterText}
        inStockOnly={inStockOnly}
      />
    </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")
);

Summary

We applied state to our application.

Next Up…

In the next video, we will continue with the sixth, and final, part of, “Thinking in React”.