Container and presentational components

Splitting pages up into container and presentational components makes the presentational component easier to reuse. The container component is responsible for how things work, fetching any data from a web API and managing state. The presentational component is responsible for how things look. Presentational components receive data via their properties and also have property event handlers so that their container can manage the user interactions.

We are going use this pattern in our React shop to split the product page into container and presentational components. The ProductPage component will be the container and we'll introduce a new component called Product that will be the presentational component:

  1. Let's start by opening our shop project in Visual Studio Code and entering the following command in a terminal to start the app:
npm start
  1. If we navigate to a product, let's remind ourselves what the product page looks like:

  1. Let's create a new file called Product.tsx that will contain our presentational component with the following content:
import * as React from "react";

const Product: React.SFC<{}> = props => {
return <React.Fragment>TODO</React.Fragment>;
};

export default Product;

Our presentational component is a function component.

  1. Presentational components receive data via props and also delegate event handling via props. So, let's create props for the product data item, whether it has been added to the basket, and the handler for adding it to the basket:
import * as React from "react";
import { IProduct } from "./ProductsData";

interface IProps {
product: IProduct;
inBasket: boolean;
onAddToBasket: () => void;
}
const Product: React.SFC<IProps> = props => {
return <React.Fragment>TODO</React.Fragment>;
};

export default Product;
  1. If we look at ProductsPage.tsx, we are going to copy the JSX for when there is a product that is the React.Fragment section. We then paste this into the return statement for our Product component:
const Product: React.SFC<IProps> = props => {
return (
<React.Fragment>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p className="product-price">
{new Intl.NumberFormat("en-US", {
currency: "USD",
style: "currency"
}).format(product.price)}
</p>
{!this.state.added && (
<button onClick={this.handleAddClick}>Add to basket</button>
)}
</React.Fragment>
);
};

We have a few reference issues now to resolve.

  1. Let's define a product variable above the return statement to resolve the issue with the product reference in the JSX:
const product = props.product;
return (
...
)
  1. Whether the product is in the basket is passed in via the inBasket prop now. So, let's change the conditional around the Add to Basket button to use this prop:
{!props.inBasket && (
<button onClick={this.handleAddClick}>Add to basket</button>
)}
  1. The final reference issue we need to resolve is with the handler for clicking the Add to Basket button. Let's first create a handler that simply calls the onAddToBasket prop:
const product = props.product;

const handleAddClick = () => {
props.onAddToBasket();
};

return (
...
);
  1. We can remove the this reference where we reference this handler in the JSX:
{!props.inBasket && (
<button onClick={handleAddClick}>Add to basket</button>
)}

That's our Product presentational component complete for the time being. So, let's reference our Product component in our ProductPage component.

  1. First, let's import our Product component into ProductPage.tsx: 
import Product from "./Product";
  1. Now, let's replace the section we copied in the JSX with our Product component:
return (
<div className="page-container">
<Prompt when={!this.state.added} message={this.navAwayMessage} />
{product ? (
<Product
product={product}
inBasket={this.state.added}
onAddToBasket={this.handleAddClick}
/>
) : (<p>Product not found!</p>)}
</div>
);

We pass the product, whether the product has been added to basket, and the handler for adding to the basket together as props to the Product component.

If we look at the shop again and go to the product page, it will look exactly the same.

So, we just implemented our first container and presentational components. Container components are great as the top-level component within a page, fetching data from a web API, and managing all the state within the page. Presentational components just focus on what needs to be rendered to the screen. A benefit of this pattern is that presentational components can be used elsewhere in the app more easily. For example, our Product component could fairly easily be used on other pages that we create in our shop. Another benefit of this pattern is that presentational components are generally easier to unit-test. In our example, our Product component is a pure function and so unit-testing this is simply a case of checking that the output is correct for different inputs because there are no side-effects. We'll cover unit testing in detail later in the book.

We'll continue to enhance our product page in the next section by adding reviews to it and adding tabs to separate the product description from the reviews.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.121.214