React e Shop
React e Shop
React e Shop
This is a Leanpub book. Leanpub empowers authors and publishers with the
Lean Publishing process. Lean Publishing is the act of publishing an
in-progress ebook using lightweight tools and many iterations to get reader
feedback, pivot until you have the right book and build traction once you do.
2016 Manav Sehgal
Contents
Easy Start React . . . . . . . . . . . . . . . . . . .
Eshop feature goals . . . . . . . . . . . . . . .
React learning goals . . . . . . . . . . . . . . .
Start React app in three easy steps . . . . . .
Windows and Node . . . . . . . . . . . . . . .
Structure of the React app . . . . . . . . . . .
App.js component definition . . . . . . . . .
index.js root component . . . . . . . . . . . .
package.json dependencies . . . . . . . . . .
Integrating React Bootstrap . . . . . . . . . .
index.html template . . . . . . . . . . . . . .
ESLint for JS guidelines and syntax checking
Flow static type checking . . . . . . . . . . . .
Build and deploy . . . . . . . . . . . . . . . . .
Deploy using Firebase . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
.
1
.
1
.
3
.
4
.
5
.
6
.
8
.
8
.
9
. 13
. 14
. 16
. 18
. 20
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
22
22
22
23
24
25
25
25
Product Summary . . . . . . . . . . . . . . .
Eshop feature goals . . . . . . . . . . . .
React learning goals . . . . . . . . . . . .
Product summary design . . . . . . . . .
Product summary schema . . . . . . . .
ProductSummary.js custom component
Prop validation . . . . . . . . . . . . . .
Constructor, state, and bind . . . . . . .
Event handler methods . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 26
. 26
. 27
. 28
. 29
. 30
. 30
. 31
. 32
CONTENTS
33
42
https://reacteshop.com
https://github.com/manavsehgal/react-eshop
Get the code. You can clone the source for this entire book, change to app
directory, checkout just the code for this chapter, and start the app to launch
the local version in your default browser.
git clone https://github.com/manavsehgal/react-eshop.git
cd react-eshop
git checkout c01-easy-start-react
npm start
The Create React App scaffold generator finishes with this message.
Success! Created react-eshop at .../react-eshop.
...
Happy hacking!
Step 3: Thats it! Now you can run npm start command in the Terminal. This
in turn fires up your browser to render your app at http://localhost:3000 local
address.
3
https://facebook.github.io/react/blog/2016/07/22/create-apps-with-no-configuration.
html
https://github.com/nodejs/node-gyp/issues/629
https://c9.io
6
https://nodejs.org
5
The scaffold also contains minimal configuration within package.json and only
one development dependency in the form of react-scripts package.
Class definition. ES6 class statement defines the custom App component and
extends Component we import from core React.
class App extends Component {
JSX. The render method returns JSX which looks like HTML. JSX structures React
component hierarchies, connects UI with event handlers, and specifies data
flow between components.
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
To get started, edit ...
</p>
</div>
);
}
Native components. The HTML-like tags are actually native React components
wrapping the respective HTML tag features. Yes, even <p> is a native React
component wrapping the actual HTML DOM tag for paragraph.
<p className="App-intro">
To get started, edit ...
</p>
Note the subtle differences in use of className instead of class which is not used
as it is a JavaScript reserved keyword.
Composition. React is all about component hierarchies. You will notice two
kinds of component compositions in this example. App custom component
owns div native React component. The div component in turn is a parent to
children div and paragraph components. Subsequent div component identified
by className App-header is parent to img and h2 child components.
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
Children. Components can be self-closing like the img component or have children components or text like the paragraph and h2 components, with enclosing
tags. These components can then refer to the enclosed components or text
using this.props.children within their own component definition.
<h2>Welcome to React</h2>
Props. React components can pass data from owner components like custom
App component in this case, into their owned components, like the img native
component. This data is passed using what are known as props or properties.
Examples of props passed in this file include className, src, and alt properties.
<img src={logo} className="App-logo" alt="logo" />
We will learn more React ES6 concepts as we code along Eshop app in this book.
ReactDOM.render(
<App />,
document.getElementById('root')
);
package.json dependencies
The package.json configuration file represents the project dependencies used
when you run the app or build.
"devDependencies": {
"react-scripts": "0.2.0"
},
"dependencies": {
"react": "^15.2.1",
"react-dom": "^15.2.1"
},
There is only one development dependency, react-scripts. Runtime dependencies which are loaded in the browser include react and react-dom core libraries.
Step 3. Import and use required React Bootstrap components within your app
component.
Let us rewrite App.js using React Bootstrap custom components replacing all of
React native components.
We start by importing the required components from react-bootstrap. Note
that selectively importing components reduces the runtime size of our app and
in turn improves performance.
7
8
https://react-bootstrap.github.io/
http://getbootstrap.com/
10
We no longer need App.css as all styles will come from React Bootstrap.
Next we rewrite the render method. If you are familiar with Bootstrap, you
will notice many similarities in the component hierarchy and naming. You will
also appreciate that the React JSX code is way more concise than plain HTML +
Bootstrap code. This is thanks to good React design patterns following by React
Bootstrap.
class App extends Component {
render() {
return (
<div>
<Navbar inverse fixedTop>
<Grid>
<Navbar.Header>
<Navbar.Brand>
<a href="/">React Eshop</a>
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav pullRight>
<NavItem href="//github.com/manavsehgal/react-eshop">
Code
</NavItem>
<NavItem href="//leanpub.com/reacteshop">
Book
</NavItem>
</Nav>
</Navbar.Collapse>
</Grid>
11
</Navbar>
<Jumbotron>
<Grid>
<h1>Easily Reusable Eshop in React</h1>
<p>Eshop written in React, ES6, and Firebase.</p>
<p><Button bsStyle="success" bsSize="large">
Learn more
</Button></p>
</Grid>
</Jumbotron>
</div>
);
}
}
export default App;
If you are unfamiliar with Bootstrap, we suggest starting with their well documented website, followed by React Bootstrap documentation to note the similarities and differences.
Let us walk through the important bits to understand the new App component.
We are adding two important components Navbar and Jumbotron to deliver the
Eshop feature goals for this chapter.
The Navbar.Header and Navbar.Brand are React namespaced components9
representing children of Navbar parent component.
Note that the Grid component is only available in React Bootstrap, however it is
actually wrapping container component from Bootstrap using bsStyle='container'
default property.
The boolean properties without values inverse, fixedTop, and pullRight can be
read as props set to true value. When these properties are not specified they
default to false value.
Note that the new App component renders a mix of React Bootstrap custom
components and React native components. Effectively mixing two component
libraries written by different vendors to create your custom app, using a single
component hierarchy. This is the true power of React. Composition is so intuitive that you miss how easy this integration is compared with other solutions.
9
https://facebook.github.io/react/docs/jsx-in-depth.html#namespaced-components
12
13
index.html template
To change the browser tab title we need to change the page template defined in
the index.html template.
The page template can be used to add webfonts, meta tags, or analytics, just like
you would do in the default HTML file.
You can also use EJS template engine10 syntax to make really powerful templates
for your React apps.
Let us add some EJS to configure meta tags targeting Facebook, Twitter, and
search engines.
<%
var title = 'React Eshop | Easily reusable Eshop in React';
var description = 'Reusable Eshop app in React, complete with a shop\
ping cart, product catalog, and search.';
%>
<title><%= title%></title>
<meta name=twitter:title content="<%= title%>">
<meta property=og:title content="<%= title%>">
<meta name="description" content="<%= description%>">
<meta name=twitter:description content="<%= description%>">
<meta property=og:description content="<%= description%>">
<meta name=twitter:card content=summary />
<meta property="og:site_name" content="React Eshop" />
<meta property=og:type content="website">
In the HTML code for our templates head section we are using EJS tags <%
%> to add JavaScript. The EJS <%= %> tag inserts result of JavaScript expression
following the equal sign within the HTML.
10
http://www.embeddedjs.com/
14
As you save App.js after making the above change, you will notice the Terminal
starts displaying warnings.
Compiled with warnings.
Warning in ./src/App.js
.../react-eshop/src/App.js
1:17 warning 'Component' is defined but never used
no-unused-vars
This warning is somewhat helpful. You can get better insights by integrating
ESLint warnings within your editor.
To integrate ESLint within Atom editor use the following instructions.
Step 1: Add global dependencies required for ESLint editor integration. This
workaround is required until ESLint fix it.
npm install -g eslint babel-eslint eslint-plugin-react
npm install -g eslint-plugin-import
npm install -g eslint-plugin-jsx-a11y eslint-plugin-flowtype
Step 2: Install Linter ESLint Atom package. We also install base linter package
for helpful in-place notifications.
15
Step 3: Edit Linter ESLint settings to check Use Global ESLint installation option.
Now as you make coding mistakes which ESLint catches, you will start getting
hints right within your editor. This approach is more intuitive than console
warnings.
16
Facebook promises they will consider integrating more tightly with Flow in the
future to avoid this workaround.
To help understand the value of Flow let us introduce a type casting bug into our
app.
We can mock a const called version and try to multiply an integer with a string.
render() {
const version = 1 * "beta";
We also display this version within our app title like so.
11
12
https://flowtype.org/
https://github.com/facebook/flow#installing-flow
17
To have Flow examine this file we need to add a // @flow comment at the top of
the file.
Next we go to our Terminal and run npm start command if it is not already running. You will notice that the app runs just fine without any errors or warnings.
However, the title displays NaN where version is supposed to be rendered.
Flow can help us catch this kind of bug. Now open another Terminal window or
stop your app and run flow check command.
src/App.js:8
8:
const version = 1 * "beta";
^^^^^^ string. This type is incompatible \
with
8:
const version = 1 * "beta";
^^^^^^^^^^ number
Found 1 error
If you want to catch such type checking errors in-place within the editor, all you
need to do is install another package. In case of Atom editor we can install the
linter-flow package.
apm install linter-flow
Now if you introduce type checking errors in your code, your Atom editor will
provide in-place Flow notifications. How cool is that!
18
The build folder now contains production ready files. These include glyphicons
as part of Bootstrap, source map files for browser debugging, minified and
gzipped js/css files, and generated index.html from our EJS template.
favicon.ico
glyphicons-halflings-regular.448c34a5.woff2
glyphicons-halflings-regular.89889688.svg
glyphicons-halflings-regular.e18bbf61.ttf
glyphicons-halflings-regular.f4769f9b.eot
glyphicons-halflings-regular.fa277232.woff
index.html
main.2560bd07.js
main.2560bd07.js.map
main.327e47d3.css
main.327e47d3.css.map
You can now use the recommended static server to deploy the build on localhost.
19
Optionally, you can even deploy your app to Github13 following instructions
provided in the Create React App docs.
13
https://github.com/facebookincubator/create-react-app/blob/master/template/README.
md#deploy-to-github-pages
20
Firebase Features
To get started with deploying your app, you need to install the Firebase CLI.
npm install -g firebase-tools
Next you need to type firebase login in your Terminal to login to your Gmail
account. Once you are logged in using Terminal, run the firebase init command
within your app root to create the firebase.json configuration file interactively.
Select default options when prompted with exception of following questions.
What do you want to use as your public directory? build
14
http://firebase.google.com/
21
Webpack
One of the most popular and powerful JavaScript module bundler tools, Webpack17 is a versatile tool, however really challenging to configure for beginners.
The scaffold generator manages Webpack development and production server
configurations. It runs production optimized Webpack configuration when you
run npm run build command. You are running Webpack Dev Server behind the
scenes when calling npm start command.
Webpack also helps import and bundle other assets like CSS, JSON, images, and
fonts using specific loaders.
15
https://github.com/ampedandwired/html-webpack-plugin
http://www.embeddedjs.com/
17
https://webpack.github.io/
16
22
23
Hot Reloading
As we save changes to our app, you will notice that the browser tab rendering
our app also refreshes automatically. So while you edit your app in your favorite
code editor, you can keep the browser window side-by-side to view changes
interactively as you change and save each line of code. This magic happens due
to Hot Reloading. Webpack integrates hot reloading for CSS changes within your
app.
24
18
https://babeljs.io/
https://github.com/lukehoban/es6features#readme
20
https://babeljs.io/docs/plugins/transform-react-constant-elements/
19
25
ESLint
Of course as explained earlier, ESLint is integrated within console output as you
run npm start command.
ESLint rules are inspired by Airbnb guidelines23 , however less opinionated. Read
more about the ESLint rules24 supported by the scaffold generator. These rules
relate to possible syntax or logic errors in JavaScript code.
Here is a sample of such rules used by the scaffold generator by default.
Other capabilities
The chalk npm package25 adds string styling capabilities within your React apps.
The opn package26 is a better alternative for node-open to open files, websites,
spawn executables from within your apps.
21
http://postcss.org/
https://github.com/postcss/autoprefixer
23
https://github.com/airbnb/javascript
24
http://eslint.org/docs/rules/
25
https://www.npmjs.com/package/chalk
26
https://www.npmjs.com/package/opn
22
Product Summary
Starting this chapter we will build components required for our Eshop. We will
follow bottom-up design for our component hierarchy development, starting
with atomic child component, going up the tree to compose more complex
custom components.
This is what our Eshop will look like at the end of this chapter.
Product Summary
27
27
28
https://reacteshop.com
https://github.com/manavsehgal/react-eshop
Product Summary
28
Product Name
Price in US$
Referral (true for referral, false for direct sale)
Referral link (in case referral=true)
Product thumb
Category (Gig | Book)
Featured (if featured display in header)
Product Summary
29
products.json
There are two important considerations when designing your React data schema.
Unique ids. We are defining a JSON schema with sequence of product ids. This
helps us in two ways. Firebase stores and retrieves this schema optimally. The
ids can also be used as unique keys for iterating React components in a list or
product catalog. This is a React requirement for optimal virtual DOM updates.
Read more at the Facebook reconciliation29 article.
De-normalized. We are also keeping the schema relatively flat or de-normalized (as opposed to hierarchical). This helps in making our JavaScript code referencing the schema more readable. Firebase queries consume fewer resources,
as we could monitor ids to return just the data for that specific id, if that product
changes.
{
"1": { "name": "React Eshop", "category": "Book", "price": 9.99,
"description": "Easily reusable Eshop in React, ES6, and Faceboo\
k.",
"link": "https://leanpub.com/reacteshop", "referral": true, "fea\
tured": true},
"2": { "name": "Setup Webpack React", "category": "Gig", "price": 25,
"description": "Development environment setup using React and We\
bpack."},
"3": { "name": "React Speed Coding", "category": "Book", "price": 9.\
99,
"description": "Develop custom UI library in React, Flexbox, and\
PostCSS.",
"link": "https://leanpub.com/reactspeedcoding", "referral": true\
},
"4": { "name": "Custom React Stack", "category": "Gig", "price": 95,
"description": "Custom React tech stack based on your project."},
29
https://facebook.github.io/react/docs/reconciliation.html
Product Summary
30
You will notice we are missing product thumb in this fixture. We are right now
mocking or rapid prototyping our app so we will make do with placeholder
images to distinguish between a Book and a Gig.
As we design production ready schema in Firebase, we may use Firebase Storage30 to store and retrieve our Eshops media assets in a scalable way.
Prop validation
Now it is time to define the product summary schema as shape for our component. We do this by defining property type validations and default properties.
30
31
https://firebase.google.com/docs/storage/
https://facebook.github.io/react/docs/reusable-components.html#prop-validation
Product Summary
31
The isRequired flag indicates we expect these properties to be set by the calling
component up the hierarchy. We set sensible defaults for props that are not
required.
Also notice that we are writing a shorthand version of class declaration prefixing export default. Saves a few keystrokes and avoids situations where we miss
adding export default ComponentName at the end of this file.
We are also adding a new prop called display to indicate target view that this
component will render. Depending on the target view our render code will
change, making this component more reusable.
export default class ProductSummary extends Component {
static propTypes = {
name: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
thumb: PropTypes.string.isRequired,
category: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
display: PropTypes.string.isRequired,
referral: PropTypes.bool,
link: PropTypes.string
}
static defaultProps = {
link: '',
referral: false
}
Product Summary
32
scenario where user revisits our Eshop and their in-cart quantities are based on
their actions in the prior session.
We will also require two event handler methods in our component. These methods bind to this context of our component within the constructor definition.
Alternatively we could bind these methods when passing them as props within
render method. However, this is not an optimal solution when compared with
doing so in the constructor. Constructor is only called once in the component
lifetime, however render method may be called multiple times.
constructor(props) {
super(props);
this.state = { inCart: 0 }
this.getProduct = this.getProduct.bind(this);
this.productDetail = this.productDetail.bind(this);
}
The getProduct event handler is invoked when user clicks on Add to cart button
or the Visit site button depending the the product distribution mode. In case
of direct sale products we are incrementing the inCart state by one. This needs
to be done using the React setState method. Calling setState also initiates a rerender of our component UI automatically updating the new value in our Eshop.
Product Summary
33
getProduct() {
if (this.props.referral) {
window.open(this.props.link);
} else {
this.setState({ inCart: this.state.inCart + 1 });
}
}
Product Summary
34
Product summary card view OR Product summary splash view > is part
of >
* Product summary component
You can think about this design method as specialization and composition if
you come from object oriented design background. The Add to cart or Visit site
variants are specializations for button. Button in turn is composed of button
label and color.
Another way to think about designing your React components is to think of the
hierarchy as container-child-sibling relationship when visually representing
your component UI. So, label is child of button. Button is sibling of thumb. Button and thumb are contained within the product summary container. Thinking
in React32 by Pete Hunt is one of the best articles elaborating this design
method.
32
https://facebook.github.io/react/docs/thinking-in-react.html
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_
Operator
33
Product Summary
35
Switching variants
We also define the buttonSize based on where we want to display the product
summary component. The use of switch statement makes our component extensible in the future as we may add variations for displaying product summary
in list view in addition to card and splash view.
// Render this for Button bsSize
let buttonSize = '';
switch(this.props.display) {
case 'splash': buttonSize = 'large'; break;
case 'card': buttonSize = 'small'; break;
// Handle future display targets
default: buttonSize = 'small';
}
Product Summary
36
const getProductButton =
<Button
>bsStyle={callToActionStyle}
bsSize={buttonSize}>
{this.props.referral ? 'Visit site ' : 'Add to cart '}
{callToActionContent}
</Button>
Thumbnail
Let us continue designing product thumb component which is at the same
level in the hierarchy as our buttons. We are using React Bootstrap Thumbnail
component.
We need an href="#" to add hover effect to the thumbnail. The productDetail
method is bound to this component so clicking on the thumb will have the
desired effect. We are setting the image src based on prop passed on from the
owner component.
Product Summary
37
let productThumb =
<Thumbnail
href="#"
>src={this.props.thumb}
alt="{this.props.name}" />;
Product Summary
38
Next we add the second variant for our product summary, to display it as a card
with a product catalog. We use the Well component to draw the container for
our card. Rest of the view design is similar to splash view.
We wrap our product summary component by returning the JSX composed in
the renderProductSummary variable.
if (this.props.display === 'card') {
renderProductSummary =
<Well>
{productThumb}
<h2>{this.props.name} <small>{this.props.category}</small></h2>
<h4 className="ProductSummary-price">
Price: ${this.props.price}
</h4>
<p>{this.props.description}</p>
{buttons}
</Well>;
Product Summary
};
return (renderProductSummary);
}}
39
Product Summary
40
}
// Get Product button event handler
getProduct() {
if (this.props.referral) {
window.open(this.props.link);
} else {
this.setState({ inCart: this.state.inCart + 1 });
}
}
render() {
// Different icon depending on Referral or Direct distribution
const callToActionIcon = this.props.referral
? <Glyphicon glyph="globe" />
: <Glyphicon glyph="shopping-cart" />;
// Display inCart quantity if not zero, otherwise display icon
const callToActionContent = this.state.inCart
? <Badge>{this.state.inCart}</Badge>
: callToActionIcon;
// Different color depending on Referral or Direct distribution
const callToActionStyle = this.props.referral ? 'primary' : 'succe\
ss';
// Render this for Button bsSize
let buttonSize = '';
switch(this.props.display) {
case 'splash': buttonSize = 'large'; break;
case 'card': buttonSize = 'small'; break;
// Handle future display targets
default: buttonSize = 'small';
}
// Render this for Get Product button
const getProductButton =
<Button
>bsStyle={callToActionStyle}
Product Summary
41
bsSize={buttonSize}>
{this.props.referral ? 'Visit site ' : 'Add to cart '}
{callToActionContent}
</Button>
// Render this for Product detail button
let productDetailButton =
<Button
>bsStyle="default"
bsSize={buttonSize}>
<Glyphicon glyph="info-sign" />
</Button>;
// No product detail required for referral products
productDetailButton = this.props.referral
? null
: productDetailButton;
// Render this for call-to-action buttons
const buttons = <p>{getProductButton} {productDetailButton}</p>;
// Render this for product thumb
let productThumb =
<Thumbnail
href="#"
>src={this.props.thumb}
alt="{this.props.name}" />;
// What to render as product summary - splash or card (default) st\
yle
let renderProductSummary = '';
if (this.props.display === 'splash') {
renderProductSummary =
<Grid>
<Row>
<Col xs={12} md={6}>
Product Summary
42
{productThumb}
</Col>
<Col xs={12} md={6}>
<h1>{this.props.name} <small>{this.props.category}</smal\
l></h1>
<p>{this.props.description}</p>
<h2 className="ProductSummary-price">
${this.props.price}
</h2>
{buttons}
</Col>
</Row>
</Grid>;
};
if (this.props.display === 'card') {
renderProductSummary =
<Well>
{productThumb}
<h2>{this.props.name} <small>{this.props.category}</small></\
h2>
<h4 className="ProductSummary-price">
Price: ${this.props.price}
</h4>
<p>{this.props.description}</p>
{buttons}
</Well>;
};
return (renderProductSummary);
}
}
Product Summary
43
them as part of the build we deploy using npm build. The images are not copied
over individually to the build folder. We also import the package.json fixture
data.
We are then assigning a const with parsed JSON string so that the products data
object can be processed by our component JavaScript.
import React, { Component } from 'react';
import ProductSummary from './ProductSummary';
import {
Grid, Row, Col,
Navbar,
Nav,
NavItem,
Jumbotron } from 'react-bootstrap';
import bookThumb from './book-mock.jpg';
import gigThumb from './gig-mock.jpg';
import productsData from './products.json';
const products = JSON.parse(JSON.stringify(productsData));
We will develop the rest of the App component using the same reverse hierarchy
method as we did for product summary component.
We first create the JSX to render the featured product. We are using JavaScript
map() method to map the products objects individual entries to an array of JSX
ProductSummary nodes.
If one of the products is featured, we will set the display prop to splash so
that the appropriate view is rendered. We need to specific key as a unique id as
explained earlier, this is a React requirement for iterating nodes, even if there
is only one in case of featured product.
The rest of the props are set to values from our JSON fixture by referencing these
using JavaScript object notation.
In case of thumb prop, for now as we are creating a mock product catalog, we will
pass the mock images we imported earlier.
Product Summary
44
The product card view is rendered in a similar manner. This time we want to
display multiple product cards nicely laid out like a product catalog mock, which
responsively resizes its contents.
const productCatalog = Object.keys(products).map(key => {
return (!products[key].featured
? <Col xs={12} md={6} lg={3} key={key}>
<ProductSummary
name={products[key].name}
description={products[key].description}
price={products[key].price}
link={products[key].link}
thumb={products[key].category === 'Book' ? bookThumb : gigTh\
umb}
category={products[key].category}
referral={products[key].referral ? true : false}
display="card"
/>
</Col>
Product Summary
45
: null
);
});
Now we are ready to create return the rendered home page view with splash area
featuring our product and product catalog area displaying product cards.
There is no change to the Navbar component JSX. We only replace the Jumbotron children with productFeatured JSX. We also define a Grid for displaying
the productCatalog JSX.
return (
<div>
<Navbar inverse fixedTop>
<Grid>
<Navbar.Header>
<Navbar.Brand>
<a href="/">React Eshop</a>
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav pullRight>
<NavItem href="//github.com/manavsehgal/react-eshop">
Code
</NavItem>
<NavItem href="//leanpub.com/reacteshop">
Book
</NavItem>
</Nav>
</Navbar.Collapse>
</Grid>
</Navbar>
<Jumbotron>{productFeatured}</Jumbotron>
<Grid>
<Row>{productCatalog}</Row>
</Grid>
</div>
);
}
Product Summary
46
}
export default App;
Congratulations! That is a lot of learning from one chapter. You just created
your first Eshop ProductSummary component. We also learnt how to rapidly
prototype the product catalog using React Bootstrap, JSX, and React.