Reactshop is an open-source online shopping application. It's main purpose is to serve as an example of React app during the React workshop, so the README file contains all steps that are taken to create this app.
Let's make some final refinements:
- Update
CategoryItem
:react-router-dom
providesLink
component that allows navigating and acceptsto
property with url. - Update
CategoryGrid
to display actual categories' names instead of ids. - Get rid of the click handler ;)
Notice that now there are two places where we would need information about
categories. Those places (URLs /
and /category/:id
) can be visited
independently so they both would need to make a request to the API.
However, we may decide to make a single call in parent component -
CategoryLayout
.
- Lift the
categories
state fromCategoryGrid
toCategoryLayout
, move the fetching logic. - Now
CategoryGrid
needs to receivecategories
fromCategoryLayout
state via props. To achieve that, replacecomponent
in appropriateRoute
withrender
. Therender
property ofRoute
expects a function in form:(props: Object) => React.Element
. - Modify
CategoryGrid
to rendercategories
from props - Update analogously the
Route
forCategoryDetails
component. Observe, that function in<Route render
will be called with props with URL metadata likeprops.match.params.id
that comes from the URL (/categories/:id
). Use it to pass appropriate category toCategoryDetails
. - Ensure that both
CategoryGrid
andCategoryDetails
rely on data received in props. Edge case: there's a moment before data is fetched (use loading pattern).
-
Run
npm install --save react-router-dom
-
In the
App
component importBrowserRouter
andRoute
from the package. -
Wrap the
App
contents with a router:<BrowserRouter> ... <Route path="/" component={CategoryLayout}/> {/* Matches path beginning with `/` */} ... </BrowserRouter>
-
Modify
CategoryLayout
: wrapCategoryGrid
so that appropriate component is shown on the matching URL:<React.Fragment> <Route exact path="/" component={CategoryGrid} /> <CategoryDetails category={this.category} /> </React.Fragment>
-
Check these two URLs:
Ensure that
CategoryGrid
is only shown on the former.
- Add initial state to
CategoryDetails
- it will store data about products - Add
loading
property to the state, default it to the true. Ensure that you're not attempting to render products whenloading
is true (display a nice message instead) - Component
CategoryDetails
should fetch products to display. Make a call to Products API and store the response. Update state withloading: false
. - Update the template to render actual products' names instead of ids
-
Create
CategoryDetails
class component. It should to inherit fromReact.Component
. -
Define a
category
member inCategoryLayout
. RenderCategoryDetails
here and pass the category to it as a property. -
Assign one of the entities that can be found in the API http://localhost:3001/categories to the
category
member (hard code the value). -
Render the
CategoryDetails
component using following template - fill it with the actual values:<div className="category-details"> <h1>[Category name]</h1> <ul> <li>[product-0]</li> {/* These are products' ids */} <li>[product-1]</li> {/* List is generated dynamically */} <li>[product-2]</li> </ul> </div>
Event handling in React is similar to that in HTML. However, the attributes are all the same but written in camelcase and we're passing a function reference to them.
CategoryItem
is now a dumb component, meaning all the business logic goes to some smart component above it, likeCategoryGrid
. Prepare a method ofCategoryGrid
and pass it toCategoryItem
as a prop - it'll handle click on the item.- Within
CategoryItem
useonClick
to handle clicking event to the rendered div. Pass the function received in the props.
For a dumb component that only accept props and render html we may simplify our code using functional components. They do not have their state nor lifecycle hooks.
- Extract
header
fromApp.js
as a functional component - Refactor
CategoryItem
to be function that acceptsprops
object and returns rendered HTML
React components can have various methods that will be called by the library
in various moments of component lifecycle. We'll use them to fetch data
from the API. Some of them are: componentDidMount
, componentShouldUpdate
,
componentDidUpdate
, componentWillUnmount
and componentDidCatch
.
- Check what the API returns at http://localhost:3001/categories.
- Clear the initial state in
CategoryGrid
. - Declare
componentDidMount
method in the component. Refer to browsers' native Fetch API to fetch data from the server. - When data is successfully fetched, update the state with
this.setState
method.
In this step we're introducing component state
- Declare class member
state
withinCategoryGrid
. As this is the component default internal state, initialize it with an empty object. - Add a
categories
property to the component initial state that will hold a list of categories to be rendered. - Render
CategoryItem
elements based on the aforementioned list. - Clean up: move the array of categories from
CategroryLayout
to the state ofCategoryGrid
. We'll no longer need the propcategories
.
- Create
CategoryLayout
component. - Render a div inside it
- Move rendering
CategoryGrid
from theApp
component toCategoryLayout
- Render
CategoryLayout
insideApp
.
- Create a
CategoryItem
component that will render the samediv
as previously was used inside the container - Render a property called
name
within the div - Use the component in
App.js
inside existingdiv.category-grid
container - Create a
CategoryGrid
component - Assume the component accepts
categories
array as property. Usemap
method to produce multipleCategoryItem
s within render method - Use the component in
App
Our first goal is to create a Homepage that a user will see when visiting our shop. It will display the page layout and categories available for shopping.
- Look around in
App.js
. It is a right place to render a grid of categories. Adddiv
container and somediv
elements inside it. You can usecategory-grid
andcategory-item
classes that already have some CSS styles assigned. Add a few categories.
The initial application was bootstrapped with create-react-app
but it also is combined with an API server made with Express JS.
In order to run the app, please first ensure that you've installed all
dependencies with npm install
, then go for npm start
.