Hello everyone, and welcome to another React
video. Today's video is going to be very interesting, where I’m going to build a drink-ordering
app project. It’s a beginner project where you’ll use React’s key features and hooks
to create UI and cart functionality from scratch. If you're new, please subscribe and press
the bell icon so you won’t miss any tutorials in the React series. Let's get started. This is the project we’re going to build
through this video, Here we are rendering dummy data, and each drink card has an add-to-cart
button, We can increase and decrease quantity of each one, and when we click this add button
it’s going to update the number in cart. If I increase the quantity of this drink and
click the add button, you’ll see the product has been added to the cart and updated badge
number. Now when I click this cart button, cart container
is going to appear, which contains the product information and total price. Now if I increase
the quantity, you’ll see total price get updated, and if I decrease the quantity, the
total price gets reduced by price. If cart has only one drink item, then decreasing the
quantity will delete that product. Let’s start building. Open your favorite
code editor mine is VS Code and open terminal and type npx create-react-app and then folder
name, I'll name my folder 'refreshCall' and now press enter. It’s going to install all
necessary files that we need for our new react app. When you are done with installing, you will
see that the React project has been installed with the folder name refreshCall or whatever
you gave it earlier. Now, lets navigate to this folder with the command cd and project
folder name refreshcall / now press enter Now go ahead and type npm start. Here we are,
we successfully installed React on our local server. Now let’s go to our project folder and there
we’ll delete unnecessary files that we won’t use. So I will delete App.test and reportwebvitals
file as well and inside the index file remove here, here and also this. Now In the App js file, you will notice some
default code, let's remove and replace with our own. And also I’ll delete these import
line as well. Also you can select font-family that you want to use in your project from
google fonts. I chose Nunito, and from here you can copy the link and go to the index.js
file which is inside the public folder, where you can paste it inside the head tag. Let’s
use style tag and select the selector to declare font-family and margin, margin is 0 and copy
font family and paste it here. Also we can change title so I’ll change
it to my app name refreshcall. Now we are ready to work on our application.
Let’s create a Header, In src folder, create a new folder called components, and inside
this folder create a subfolder called layout, and inside this folder create a file named
Header.js. The components folder will have reusable components
used to build UI. I am using extension ES7 React/Redux/GraphQL
Install it from here if you’ve not already. It’s going to help you building the app
very fastly. Now inside Header component, you can use shortcut
rafce and it’ll create an arrow function with name Header and export it as default.
Now we can import this component into App.So import Header from layout folder also return
Header element within div tag. Now inside Header component, we’ll have
a logo and a cart button where we’ll display a number of drinks in the cart. Also, I’ll
include title and content. Let’s create another folder named assets,
it’ll handle our application images. Inside this assets folder create img folder and save
logo and banner inside. So this is banner and this is my logo of this app. Inside Header component import logo variable
from our image is outside of this so I’ll go level up and assests folder and image folder
and then file name refreshcall.png The return statement contains JSX code where
I will structure header for symantic reason. Inside this header write navigation section
using nav, inside navigation we’ll have our logo and cart button so let’s define
an img tag for displaying a logo and then src attribute specifies the source of the
image, which is stored in the logo variable. The alt attribute provides alternative text
for the image, which is helpful for accessibility so I’ll write my app name refreshcall then
close this. We’ll define our CartButton shortly.
Let’s defines a div HTML element outside of this nav it will hold the main content
of the header. Write h2 element for title and inside the
title let’s create nested element span around refreshing, later we’ll apply the class
to change its color to make it stand out. Lastly, define p a paragraph element to describe
what the users can expect from the selection of drinks offered by the application. So here Header component contains navigation
and content section with a title and a descriptive paragraph. Let’s see, it works.Now it’s not looking
what we want to so I’m going to use css for styling, I’ll use CSS module throughout
the App. There are many advantages of using CSS modules.
A CSS module is a simple CSS file, a technique used in React to style individual components.
we just need to add .module before the .css extension to the file, for example instead
of style.css, save file as style.module.css. I’ll recommend you to keep watching and
you’ll understand it better. Let’s create separate CSS file for Header
component, named Header.module.css and import this file to Header component as follows.
Here styles is an object, Instead of styles, you can use headerStyle or any other related
name. We insert classes similar to using local variables in Javascript.
In header tag define className attribute and because styles is an object we’ll use curly
braces and pass styles.header inside, will be responsible for styling this header section.
Now inside the Header.module.css file we’ll declare styling just we used to.
targets elements with the class name "header" set the maximum width of the element to be
100%, set the height of the element to 400 pixels, It determines how tall the header
section will be on the page. sets the background image for the header. specifies the URL of
the background image. Make sure the path is correct relative to the location of the CSS
file. So it’s outside of the component and inside the assets folder and inside img folder
and the name banner.jpg no-repeat, prevents the background image from repeating. center/cover,
This combination centers the background image both horizontally and vertically within the
element. As the module follows the rules of local scope, we can have a header class in
a different component with a totally different style. Now let’s see, perfect!
Now I’ll define nav-items class to style the navigation section. So display flex, justify
content space between, align items center, padding 10px top and bottom 5rem left and
right, Now we’ll have content class to style text.
For title I’ll use different font-family so let’s go to Google Fonts and search Montserrat
click to add button and now copy this import from here and paste it at the top of Header.modul.css
file. Also copy this font-family and paste it here and font-size is going to 1.25rem
then text align center Next define content class with p paragraph
for to style font size 1rem. Here select the class .text-color to change
the color of the text. The color is going to shade of purple.
To use these styles in that specific element, let’s go to Header component, classes are
defined in the styles object imported from the Header.module.css file. If a class is
a single name, we access the selector class as `styles.header` and if it uses a hyphen
like nav-items, we use a square bracket, here define class name attribute and styles and
then use square brackets with single quotes then nav items. Define the class name attribute
and styles object with the content class that we defined earlier and text color class, className
attribute and style object and the class text-color. So here, if it’s a single word we are going
to use as it is and if it’s hyphen we use square brackets.
Now go to browser and here everything is good. Also here I’ll include cartButton a custom
component so inside the layout folder create a new file and name it CartButton. Write rafce
and press enter remove this from here we don’t need anymore with the updated version.
And then return jsx with the button element And this button element will take another
nested element span there we’ll include cartIcon leave it as it is for now,
specify another span for badge and number is 0.
Now create another new file inside layout folder named CartButton.module.css and I’ll
write css code here. Import this file into Header component as follows.
Now we’re applying a class using the className attribute the class will be imported from
styles object . I’ll use square brackets and inside square brackets the class is going
to cart-container. On span define className attribute and class is going to styles.badge,
We’ve this cart-container class inside CartButton component so select this class and give the
position relative, background is going to shade of blue, padding .5rem, border-radius
5px and the cursor is going to pointer. We’ve another class badge so select this
class. It's likely used to style a badge element, which I’m going to use to show the number
of items in a cart. For creating the circular shape, I need a
fixed width and height, so width is 25px and height is 25px as well. Position is going
to absolute to positions the badge absolutely within its nearest positioned ancestor, border-radius
60% this property rounds the corners of the badge to make it circular, color is white
this sets the text color of the badge, sets the background shade of purple, top property
is going to -12px and right is -12px as well these properties position the badge 12 pixels
above and to the right of its normal position, creating an overlapping effect, display inline
flex, justify content center and align items center, center the badge text both horizontally
and vertically within the badge element font-size 12px and set the font-weight bold, box-shadow
add the subtle box shadow to the badge, giving it a slight elevation effect.
And last step we need to include CartIcon nested component inside ButtonCart component
for this I’ll create another new folder inside components folder and folder name is
going to cart, Inside this cart folder we’ll have our cart container and cart items. Let’s
create new file, CartIcon js and define functional component CartIcon also export it as default.
CartIcon returns jsx structure svg-based cart icon, you can download svg code from online
and paste that code inside parenthesis. import CartIcon into this file and between span return
CartIcon element also inside the Header component import CartButton and return CartButton element,
… … and move CartButton component inside layout folder. Now save all of that, it's
good! Now let’s create reusable drink cards where
we’ll display the information for each drink, including the name, price, and quantity controls.
Go to vs code create another new folder name it drinkCard and inside folder create new
file called MenuItems.js Inside this file we’ll store a list of different
types of drinks in a simple JSON format Define a functional component MenuItems
and export it as default. Now let’s create an array of objects representing different
drinks, const drinks data and the array, Each object will contain properties like id,
name, description, price, and image. Let’s define our first array object with id 1, name
blue lemonade, description cool and tangy delight, price 2 dollar, because I’ll have
8 drink items so copy this and paste this here 8 times. Replace the id with 2 … … now
I’ll replace name blueberries smoothie and description creamy and refreshing, price is
3.5 dollar. Next name is chocolate shake, description heavenly chocolate milk shake
for chocolics, price is 2.1 dollar. Remove blue, lemonade and description is going to
enjoy the tangy refreshments of lemonade, name Mojito description combines the flavors
with a vibrant blue color twist price 1.5 dollar, here name is goint to min
tea and description naturally, sweet and free of caffeine and the price is 1.9 dollar, name
raspberry smoothie and the description burst of flavors with raspberry smoothie price 2.7,
and the last name is going to strawberry shake and the description is sweet creamy delight
and the price 2.4 dollar. Each drink item also needs an image. Let’s save images inside
assets folder. Create another new folder `drinksItem` and paste images inside. Here we’ve different
images, you can get free images from pixabay and pixels. Let's import these images into
MenuItems so we can access them. blueLemonade is variable name which is inside
asset folder which is outside of component so level up one folder and second and then
assets and drinksItem folder and file name blue lemonade, here I’ll copy this and paste
it here and change the variable name to blueBerries and the file name is goint to blueberries-smoothy.jpg
paste again and change the variable name with the chocolate shake and the file name and
here is going to lemonade variable and change the file name to lemonade.jpg another the
variable is going to mojito and the file name is mata so I’ll change it to mata as well
and here I’ll change name to mata, paste again mint tea variable and change file name,
again, it’s going to raspberry change the file name, another image with the name strawberry
shake and change the file name as well. Inside array of object place new image property
and value is going to variable that we created earlier, it’s create a reference to an imported
image file. I’m going to add the image property to all of my object with the assigned variable
name. Now that we have created dummy data, it is time to define a section in jsx so return,
write the section element, and for styling, I will create a new file within drinksCard
folser called MenuItems.module.css, and here I’ll import it into the MenuItems component.
So import style object and file name is MenuItems.module.css I can now define className with a style object
to access items. Here I give class items so select items class
set max width 65rem and width 100%. This makes sure the element takes up the full width of
its parent container display flex and grid template columns set to repeat with the value
is auto-fit … 300px and 1fr, margin 1rem auto, grid gap 20px.
Inside MenuItems component I’ll use map() javascript method to iterate array of drink
data objects which are stored in DRINKS_DATA constant.
This is javascript expression so enclose it with curly braces.
Map accepts arguement so here I’ll specify drink and here just for example I’ll use
paragraph and return drink.name. To display on page I’ll import MenuItems
component insite the App and return, below Header element. Now Let’s see and you’ll
see it displays each drink item name on page. Now I want to display a list of drink cards
based on data. Go to vs code, inside drinkscard folder create new file DrinksCard.js and another
file for CSS module here DrinksCard.module.css . Now we’ll import style object from the
file name DrinkCard.module.css now define functional component with const DrinksCard
and export it as default. remember instead of styles you can name it
as you want. Import styles form DrinksCard.module.css
In MenuItems component, instead of paragraph, we’ll pass nested component DrinksCard to
dynamically render a DrinksCard component for each drinks. So import it at the top and
here DrinkCard element. On DrinksCard component specify attribute
key and value drink.id, It's required for elements rendered inside a loop or map function
to help React keep track of each element uniquely. id drink.id allows the DrinksCard component
to identify which drink it's rendering. Image with the value drink.image provides
the image source for each card name={drink.name}, description: {drink.driscription},
price: {drink.price} And the name to display the name, description
drink.description, and then price. Each card gets its unique data (id, image,
name, description, price) from the corresponding drink object in the data array.
Now we can access these properties from DrinksCard component.
So here inside DrinkCard component pass props as an argument inside parenthesis. Return
div and on div specify className attribute with style object I’ll create a module css
file shortly. And here the class name is going to drink-card, I’m using hyphen so I’m
using square brackets. Let’s display the drink’s image with the src attribute we’re
accessing from the parent element alt attribute with the props.name and the className is going
to style with the class drink-card image here another div with the className attribute with
style object with the class drink-card content contains the name and description, so here
h3 with the className style drink-card name and it’s going to display the drink card
item name and the paragraph with the className attribute style object with class drink-card
description.It’s going to display the drink description
Then another div with className style object and the class is drink-card foother
represents the footer section of the card, which includes the quantity controller, price,
and an "Add" button. Here drink cart quantity is a placeholder
for the quantity controller. Here write className style and the class is drink-card quantity
and here I’ll give it a class name style object and class is price and here the className
attribute and with the style object and class is going to drink-card add btn.
Now let’s give our cards some styling. Let’s select drinks-card class set position relative,
display flex, flex direction column, align items center, set background image for the
card, creating a linear gradient from transparent to a dark blue, specify 180deg transparent
dark blue, set color light gray color, add a bottom margin of 1rem, card’s width to
280px, set height auto, border-radius 8ox giving it a slightly curved appearance with
8px border radius. Border 1px solid gray color light gray, box shadow 0 5px 10px and rgb,
applies the box shadow to the card, transition transform 0.3s for smooth transition effect.
Now I’ll select class ‘drinks-card__image`, width 100%, height 180px, object-fit cover,
object position center defines the position of the image within its container, border
radius create a slightly rounder bottom right corner, giving the image a subtle stylistic
effect. Now select the class `drinks-card__content`, set padding 10px and 20px.
Select class `drinks-card__name`, set font size 18px, font weight 700, letter spacing
2px there will be an extra 2 pixels of space between each character in the text. Select
class drinks-card__description, set font size to 14px and margin bottom 20px.
Select class `drinks-card__foter`, sets the display property to flex, sets the width of
the element I’m going to usecalc() function to calculate the width 100% minus 20 pixels,
padding 10px, justify content space between, align items center.
Select class drinks-card__quantity, sets border radius to 2px, background light gray, font
size 1rem, font weight 400, color black. Select class `drinks-card__add-btn`, sets
the background color a shade of purple, padding top bottom 10px right left 20px, color white,
border 0, box shadow. I’ll also apply hover effects on card, transform property and translate
Y with the value 10px. when you hover over a .drinks-card element,
it will move 10 pixels down from its original position, creating a subtle "lifting" or "floating"
effect. Save the file, let’s see our work, fantastic!
This is a simple UI of drink card and to allow increasing and decreasing quantities, I’ll
add button instead of text and instead of price text here I want to add actual price
that we defined earlier. So let’s go to DrinksCard component and here I’ll add curly
braces defined props.price, here we’re accessing the price from parent compoent MenuItems save
it and let’s see and here we are. We render successfully the actual price that we defined
earlier when we created dummy data. Now to add quantity controller let’s add
a nested component inside DrinksCard called QuantityController.js create new file QunatityController.module.css
to styling component, let’s import styles object from QuantityController.module
and type rafce to automatically create arrow function and remove this, I’ll define classes
with style object and later we’ll add code to module.
Now in return statement I won’t use div because I don’t need wrap it within parent
element and also it’ll add extra node to DOM instead you can use Fragments or empty
tag, here, I’ll use Fragment and import it from react library and instead of div replace
it with fragment. Now within these tags create a button element with a class name styles
object quantity__btn. This button is to decrease the quantity, I’ll use minus symbol you
can import icon as well. Now define an input field where the user can
enter the quantity. an input of type number, set min attribute to 1 value is 1 and give
it a className styles class quantity_number I’ll copy this her and use symbol plus again
you can use plus icon and here we've got a div wrapper with className “drinks-card__quantity”,
inside this update with the child component, so here import QuantityController from file
QuantityController and return element QunatityController as well.
Just like DrinksCard module let’s add some css code to QuantityController.
We’ve two same classes quantity__btn and let’s select this and sets border to none
remove default style and sets background color to transparent, font size, font weight 500,
margin 4px and 6px, cursor posinter. Then select class quantity__number and sets
display property with inline block, width of 25px, height of 100%, font size 1rem, margin
0 10px, text align center, border none, background transparent.
Let’s see and exactly look like how we desired. Now I want to add logic to drink order app,
when I click on plus button the number should increase and when I click on minus button
the number should decrease by 1. Also I want to track the current quantity value.
To handle quantity I’ll use state update logic. Because QuantityController is inside
the DrinkCard component so I’ll handler logic inside the parent component, let’s
component useState to update logic Import useState form react
and declare const with destructuring pattern inside this specify state variable itemQuantity
and state update function setItemQuanity call useState with initial value 1.
Now Declare the handler for Increasing item quantity, handleQuanIncrease, inside this
function we’ll pass setItemQuantity to update the quantity state. It takes the previous
state prevQuant, and increment it by 1. This ensures that we're always increasing the quantity
by 1. Now I’ll declare function for decreasing
item quantity, so let’s const keyword handleQuanDecrease. Inside this function, first check if the current
itemQuantity is greater than 1. This check ensures that we don't go below a quantity
of 1 because we don't want negative quantities. If the condition is met, specify setItemQuantity
again it takes previous state and then calculates the new quantity, use Math.max method and
pass prevQuantt - 1 and 0, this decreases the quantity by 1 but prevents it from going
below zero. Next, define a function called handleQuantityChange
to handle changes in the quantity of an item. It takes an event object e as its parameter.
Inside this function, pass setItemQuantity, and inside this function, pass e.target that
triggered the event, in this case, an input field, value, text currently entered into
the input field. Now pass these handler function to QuantityController
component for this we’ll set attribute on QuantityController so set onIncrease
and pass handleQuanIncrease handler inside and pass second attribute onDecrease and pass
handler handleQuanDecrease handler function inside and pass another attribute onChange
and pass handler handleQuanChange handler functions on it. Also I’m going to pass
itemQuantity state variable to it to display the updated quantity dynamically. Here quantityNumber
and item quantity as value. Now we can pass props parameter to QuantityController
function and let's use those properties that we passed to our child component earlier.
On minus button declare event onClick and pass handler props.onDecrease, and inside
input declare event onChange and pass handler props.onChange. And the value is going to
props.quantityNumber, on plus button pass event onClick event with
the value props.onIncrease In browser, when I click button, the quantity increases and
decreases as expected, but I also want to update this price here by quantity.
So let’s go to DrinksCard component declare new const variable price and pass props.price
and call javascript method toFixed() and pass value 2. This is used to format a number with
a fixed number of decimal places, price.toFixed(2) formats it with two decimal places, and stores
it in the price variable as a string. In the next line we’ll converts that string back
into a number, declare const amount variable and use + operator before price to convert
the formatted string back into a floating-point number.
Now in return jxs statement instead of props.price we’ll calculate the total cost of items
based on their price and quantity. Use template literal and then specify two
dollar sign one for string and other to evaluate expression, specify curly braces and inside
performs simple multiplication operation, amount variable multiplication operator by
the itemQuantity. Save everything and let’s go to browser
increase quantity and price is updating now decrease quantity the price is updating as
well. Now I’ll Implement refs to track the quantity
input for each drink item in the DrinksCard component. This lets you access the current
quantity value when adding the item to the cart.
Let’s go to DrinksCard, and beside useState import useRef another built-in hook.
Inside component call useRef hook and pass initial value null. Declare a variable quantRef,
that holds a reference to a DOM element. Attach this ref to an input element by passing on
QuantityController. So ref and the value quantity. I’ll also update handleQuantityChange function
and within function I’m going to declare a new variable updateQuantity. To ensure that
quantity is represented as a number I’ll use parseInt() method and pass quantity that
we defined earlier and current.value, I’ll pass updateQuantity as an argument. It sets
the new quantity to the parsed integer value obtained from the input field. Here, we passed a ref to the nested component
to read the input value that is contained inside the QuantityController. To pass a ref
from a parent component to a child component, we need to use forwardRef method inside QuantityController
component. Let’s import forwardRef function from react library.
Then I’ll call forwardRef function on functional component, it accepts a render function as
an argument, so wrap component into forwardRef() and close the parenthesis, it takes parameters
props and ref, ref parameter is used for forwarding a ref from a parent component to this component.
Now we can pass ref to the DOM node, input, to read its current value.
Now we can control the quantity of items based on user input without relying solely on state
updates. Now, I want to interact with the add-to-cart
button; when I click it, this drink item should be added to cart, and badge should be updated
with the item quantity. So in DrinksCard component, declare function using const keywords name
function handleAddToCart and keep it empty for now, and pass this
handler function on button, specify event onClick handler function inside curly braces.
I’ll come back to this and add add-to-cart functionality in shortly.
Next step is to implement shopping cart functionality into drink order app. I need cart functionality
in different places, like on the product page where I’ll handle adding drinks to the cart
when I click this button and inside cart component where I’ll display added products. Since we need to access the cart state and
functions in various components, we’ll use React Context to manage the cart state and
provide it to the components that need access to it. Create a new folder inside the src folder
next to the components and name it context and create another new file inside newly created
context folder called CartContext.js. Because it is not a component, you can name it whatever
you want, using a small letter or a hyphen. Now, Call useContext at the top level of your
component to read and subscribe to context. And , define an object with const keyword
CartContext as a variable and create a new context with the method createContext, export
it as the default. Context allows you to share data across the component tree without manually
passing props down through each level of nesting. Inside createContext I’ll pass default value.
On Cart I’ll have items to display drink information like image, price and name, total
amount, and options for adding and removing items from the cart. So let’s pass object item property and empty
array that will store the items in the cart, next total amount as a property 0 representing
the total amount of price of items in the cart, next add the function addItem that takes
an item as a parameter but does not perform any action by default it's a placeholder for
the function that will be provided by the context is actually used, next pass removeItem
as a placeholder that takes an id as a parameter, and another function clearItem as a default
value. To pass context to components we need a provider.
I’ll declare provider in a separate file, so let’s create CartProvider inside context
folder where I’ll handle state. First, let’s import context into this file.
Now declare an anonymous function using const keyword and component name CartProvider. Pass
children, which is a special property that represents the child components that will
be wrapped by this context provider. Now return CartContext.Provider element and pass children
property between curly braces. To retrieve cart information for all components
in the hierarchy tree, we’ll modify the App component and wrap Provider around children
components. So I'll import CartProvider from context folder and CartProvider file name.
And instead of div I'll wrap it with CartProvider element and also I forgot to export it so
let's export default CartProvider. Now save it and you will see it is as it was before.
Next step is to handle various actions like adding an item to the cart, removing an item
from the cart, and clearing the cart and manage these state of the cart items I'll use useReduser.
You can put all of the state update logic outside your component into a single function,
called a reducer. In a CartProvider declare initial state with
the const keyword and name it cartInitialState and return a stateful value. We'll return
items with an empty array and total price value is 0.
Write a reducer function, where you will put your state logic. So const cart reducer you
can name it as you preferred, It takes two arguments, the current state and the action
object, and returns the next state: Now import useReducer hook from react library. Let’s update CartProvider, inside call useReducer
that we declared earlier and pass cartInitialState. It also returns stateful value and dispatch
functions. Let’s use const keywords, use square brackets, and write state and dispatch. Instead of state and dispatch, you can use
your preferred name like cartState and dispatchCartAction. Now I’ll declare handler function with the
const keyword handleAddToCart arrow function that takes a single argument item, this item
represents the product or drink that you want to add to the shopping cart. Within this function,
I will use the dispatch function, which is known as an action and represents the user's
intention; for example, if this event is triggered, it will send a message to reducer indicating
that the user wishes to add an item to the cart. Now pass an object, type property and
string ‘ADD_TO_CART’ and then additional property item and set to item argument passed
to the function. Now I’ll declare another event handler so
let's use const name handleRemoveItem which takes ID as an argument and within this function
specify dispatch and pass object, property type string ‘REMOVE_FROM_CART’, additional
property id set to id argument. I also want to clear items from my cart when clear button
is clicked. I’ll declare another event handler const
keyword and handleClearItem dispatch function, which only accepts property type string ‘CLEAR_CART_ITEMS’. Now on context provider, we need to specify
a value attribute, which is used to provide data and functions to the components consuming
this context. Now to pass these handler function handle add item, remove item and clear item
I'll declare object called contextValue. Define property items and value state items,
This property holds an array of items that represent the contents of the cart. such as
id, name, quantity, price, Define property total and value is going to
be state totalPrice, holds the total price of all the items in the cart. Now addItem another property and the function
name handleAddItem, that we declared earlier as a value.
removeItem property and then pass handle remove items as a value, it is called when a user
wants to remove an item, These functions are responsible for dispatching
an action to update the cart's state. Now clearItem as property and value handleClearItem.
It's often triggered when a user wants to remove all items from the cart in one action. Now on value attribute pass contextValue object,
Components that need access to the cart's data and actions can consume this context
and use these properties and functions to interact with the shopping cart. Now, we’ll write state logic to reducer
function. Inside use switch statement to determine how to handle different types of actions.
swithch takes an argument action.type, it is a string that describes type of action
being performed, then we'll have our three cases which is going to add to cart, remove
from cart, and clear cart items. So implement case add to cart, case remove from cart, case
clear cart items. Each case would depend on the specific requirements and structure of
cart state. Now I'll return default of switch statement it returns current state if action
type doesn’t match any of defined cases. Now we'll fill the code and work with these
case one by one. I'll start with the add to cart.
First, I want to see if the item already exists in the cart; if so, I will update the quantity;
if not, I will add a new item to the cart. I'll Destructure the properties of the action
item id, price, quantity from action item Now I'll heck if the item already exists in
the cart. So let’s const keyword with the name existingCartItem
state items find, it search for an item in state items array with same id, pass items
as an argument item id match with id action id.
Now in the next line I’ll calculate the new total price, use const keyword with cartItemsTotal
name and state totalPrice plus item price and multiple by its quantity, update the current
state of total price by item price by its quantity.
Now If the item already exists in the cart I’ll increment its quantity.
Let’s use if and pass existingCartItem in the parenthesis use const keyword with the
name cartItems then state.items, I’ll use map method and give an item as
an argument then check if current item, item id, being processed has same id as item you
are trying to add. If it does, it returns a new object copy of the items, quantity item.quantity
plus quantity, where it increments the quantity property by the specified quantity.
Now I’ll return an item and then we’ll update the state of items and totalPrice so
return a new state object, use spread operate which create a copy of current state object
then items property with new cartItems array, updating the cart state with the updated item
quantities and set totalPrice property to cartItemsTotal
But If the item doesn't exist in the cart I’ll add it. So here use const keyword with
the name updatedCartItems spread operator state.items and action items.
Action item represents the item that you want to add to the cart.
And the next return state items property value updatedCartItems property totalPrice cartItemsTotal.
Now to remove a drink item from the cart, I’ll update the REMOVE_FROM_CART action
in the reducer method. Let’s start by finding cart item I want to remove from cart. Use
const keyword with the cartItem then state items find pass item as an argument then item.id
match it with action.id now then define updatedTotalPrice and calculate it by subtracting the current
total price from the cart item price. So here state totalPrice minus cartItem price. Now let’s move to next part. We’ll check
if item exist in cart and also check quantity is greater than 1. If condition is met, create
a new array. Define updatedItems with the const keyword then state items use map method
on it that takes item as an argument. And for each item in the state.items array, check
if id matches action id and If it's a match, create a new object item quantity and reduce
quantity by 1. This effectively decrements the quantity of the matched item, then return
item. Finally, you can return a new state object
and items updated to updatedItems and the totalPrice updated to updatedTotalPrice. But if quantity is 1 it should be removed
from the cart, so here I’ll write else statement, create another new const give it name updatedItems
state.items and call filter method on it, state items as an argument item.id and we’ll
filters out the item with the matching id. Finally, return a new state object spread
operator items updated to updatedItems and the totalPrice updated to updatedTotalPrice.
Now, I’ll handle action type 'CLEAR_CART_ITEMS'. Let’s return, new state object using spread
operator to ensure that you maintain the current state properties that are not being changed.
Set items property to an empty array, this effectively removes all the items from the
cart, making it empty. Set totalPrice property to 0, This ensures
that the total price is reset to zero because there are no items in the cart. In shopping
cart I’ll check if blueLemonade already exist and if it does then it’ll only increment
its quantity I’ll change it to 1 to 2 and 3 and we won’t add it as a new product.
It looks good, but, how would I know if this code works? let’s implement add-to-cart
functionality. We’re using context for state management, and context provides access to
cart-related functionality, Inside DrinksCard I’ll use useContext hook to access cartContext,
import it at the top CartContext which is inside the context folder and beside useState
and useRef import useContext as well. Then call useContext inside functional component
and pass cartContext inside parenthesis. Let’s assign contextItems variable to it.
To allow us to add drinks to the cart, we need to handle Add to Cart button click event.
Let’s create object named item and extract product properties, so here id with props.id,
image props.image, name props.name, and price I’m going to pass amount as a value so here
we are. Also, I want to include quantity in this object
so let’s extract the quantity value from a ref, so here use const with the quantity
and parse it as an iteger.so here parseInt parenthesis quantRef.current.value.
Now pass quantity property with quantity value, this is done to get the quantity of the item
that the user wants to add to the cart. Now you can use console log and pass item
you’ll see selected drink item information logging into console. Finally, call addItem function from the contextItems
object (which is the context provided by CartContext), and pass the item object as a parameter to
this function. handleAddToCart uses this functionality to
update the cart with the selected item and quantity.
Now whenever we click this Add button, it’ll trigger the dispatch action and update context.
Now, I’ve a button component that serves as a cart button. it displays a cart icon
and number which is hard coded and I want to dynamically update badge with the total
quantity of items in the cart. Let’s go to CartButton component. You can access the cart state from the CartContext
and calculate the total count of items to display the badge. Also import useContent
hook and call useContent inside CartButton functional componentaccess the data provided
by the CartContext Here I’ll declare variable cartValue with
const keyword. Now I’ll use reduce method on cartValue.items
array to calculates the total quantity of items in the cart. Declare new const variable totalQuantity,
accesses the items property from the cartValue object.. Apply reduce, which is a Javascript method,
used for aggregating data in an array into a single value. Pass callback function to
reduce method. It takes two parameters, total and item, total is an accumulator variable
that keeps track of the total quantity, and item represents an individual item in items
array. Now return and in return statement, add up
total + item quantity, and after comma pass initial value 0. In each iteration, this line adds the quantity
property of the current item to the total. And totalQuantity will contain the total quantity
of items in the cart. Now in span element, I’ll pass this variable
to display the cart's total quantity in the badge on the cart button. Let’s see, click on add button and it goes
up; add another drink and badge is updated. Good!
Now let’s build cart container to display items in the cart, their quantities, prices,
and total price. Inside cart folder create a new file called
Cart.js and create another file named Cart.module.css. Inside Cart component I’ll import css module
so import styles from Cart.module.css which is in the same folder.
And here I’m gonna use styles object inside className and later I’ll write code for
css styling. Now define Cart component, it receives props
and export it as default. This component will use the cart state from
the CartContext so import it, import CartContext from context folder which is outside of component.
Now use useContext from react library as well to access the cart Context.
To retrieve the context's data, including items in the cart and the total, I’ll call
useContext hook inside component function and pass CartContext. Declare const contextItems,
it’s an object.Now I’ll render cart’s header, drinks, total, and checkout button.
Let’s structure return statement for cart component. Div is a parent element which going
to wrap whole cart container. Let’s give it attribute className and pass curly braces
and styles cartContainer as a class and inside let’s define header with className attribute
and styles.cartHeader as a class, typically, it will include the close button, title, and
clear all button. Let’s implement button element with a className attribute styles.closeBtn
as a class and here pass html entities times which is going to close icon and next title
write h2 and title cart and next add another button with the className attribute and give
it object class.cartClear, clear all as a value.
Let’s start with header. Now I’ll wrap drinks, total and checkout
within another div, div with className attribute styles,cartItemsContainer
Here I’ll check if item existed in cart and If there are no items in the cart, it
displays a message indicating that the cart is empty
So it’s javascript method so I’ll pass curly braces and then contextItems object,
we’re going to access the items from the cartContext object. Pass contextItems.items.length
is equal to zero and use ternary operator with the parenthesis and pass paragraph inside
with p and the message is going to there are no items in the cart and colan anohter parenthesis
so specify unordered list ul element with className style cartItems. I’m going to
use javascript expression so use curly braces, and I’ll loop over the items that we retrieve
from contextItems and render each item. I’ll pass contextItems.items and call map method
on it which is going to take item as an arguement and return … item.name and also import this
cart Items inside the App at the top and return
cart element in jsx as well. Let’s see if it’s working, It is, and
here let’s add items in the cart and it’s working as well. It’s not looking good because
we haven't declared any styling. We’ll in shortly. Now let’s define total section.
So specify div with the className styles.total and pass span inside with the total value
and another span with the 0 value. And then checkout button so add button with
the className attribute and styles.checkoutBtn as a class and the value is going to checkout.So,
we've got className with style object and I’ll use these class selectors inside module. Inside cart.module.css file I’ll write code
for styling cart. Select class cartContainer, this is main container.
set background color to light gray and set fix width of 430px. Select cartHeader class and write style for
header. Let’s use display flex for alignment, justify-content space between algin items
center, sets margin bottom 1.2rem and padding .9rem, set color.
Now I’ll style close button in cart header. Select class cartCloseBtn. Sets background
color of the button to transparent, removes border, By setting it to "none," there won't
be any visible border on the button. Set cursor to a pointer, sets the font size of the text,
font weight 800, and text color to black. Now I’ll remove spacing around title, select
parent class cartHeader and h2 tag and set margin to 0.
Now select class cartClear to style the clear button in the cart header.
sets the background color of the element to a very light gray, rounds the corners of the
element by applying a border radius of 4 pixels, adds a padding of 0.5 rem, removes any border
from the element, and changes the mouse cursor to a pointer.
Now let’s styles the container that holds the cart items. Select class cartItemsContainer Sets border-top-left-radius to 35px and border-top-right-radius
to 35px. These properties set rounded corners only on the top-left and top-right corners
of the element. Now sets the box-sizing model for the element to "content-box, sets the
background color of the container to a dark gray color, adds padding of 1.5 rems, sets
the text color inside the container to a light gray color sets the height of the container
to 100% of the viewport height. Now select class cartItems to styles the list
of cart items. It removes padding, so padding 0 and margin to 0, sets a maximum height 25rem,
and overflow auto. Now select total cost class, display flexbox
for alignment justify content space between, it creates space between the flex items, align
items center, font size 18px, font weight bold, font style italic and margin 1rem 0.
Now styles the checkout button, we’ll define background color to specific shade of purple,
display block sets button to be displayed as a block-level element, width 100%, padding
.9rem, border-radius 25px, border 0, box-shadow, text color white, font size .9rem, font weight
bold, and letter spacing 1px.Let’s save it all and see result in the browser now it’s
clean and visually appealing. I also want to add divider between total cost
section and checkout button for that let’s add another div inside cart component. So
div className attribute styles object cartDevider as a class.
And inside module I’ll style devider line, so select class cartDevider
Sets border 1px solid light gray and margin 2rem 0 to create separation. Let’s see,
perfect! Instead of just rendering name of the drink,
I want to render each drink with its name, price, image, quantity, and buttons. Let’s
create another component within cart folder and name it CartItem js, It’ll be responsible
for an individual drink in cart. Also, create another new file named ‘CartItem.module.css
responsible for styling. Inside CartItem import module with style object
Now declare functional component with an anonymous function
and pass props as an argument because it’ll receive properties from Cart component and
export it as default. Now inside Cart componet instead of p element return nested component
CartItem. So let’s import CartItem at the top and
return nested element CartItem, I’ll pass various props to provide information about
drink. So let’s pass key with the item id, id attribute with the item.id again name property
with the item.name price item.price image item.image and quantity item.quantity
Now inside CartItem component return jsx li element and give it className attribute and
apply class style cartItem, I’ll define classes in imported CSS module shortly.
Now Within list item define div element give it className attribute and apply style cartDetails.
Display image of item with img element specify src attribute and set it to props image and
alt attribute set props name to provide alternative text. Now define h3 tag and value is going
to props name, displays the name of the item. Define span element with className attribute
and specify class style price and pass expression props.prece. Create another div element outside
this div with className attribute and class style itemQuantity, to handle quantity of
drink in cart. Now inside this div declare button to decrease the quantity of the item
in the cart I’m using minus sign you can import icons, Declare span element it displays
the current quantity of the item. To allow user to increase the quantity of the item
in the cart I’ll declare another button with plus sigh, It’s going to receive props
from Cart component and these buttons will be responsible for add and remove items very
soon. Now, I’ll define style inside module let’s
go to CartItem.module.css file. We’ve defined classes already so I’m going to just use
these as class selectors. First, let’s define styles for element with class name cartItem
Set background dark grayish, set text color inside cartItem to white, add border 1px solid
black, add margin bottom of 1rem, padding of 0.9rem, romove default style type with
none from list items, use display flex to make element flex containers, use flex property
justify content space between, and set align items center. Now select element with className cartDetails
and use properties display flex, and align-items center.
style images within the .cartDetails container to be small, circular thumbnails with a width
and height of 50 pixels and border radius 50%.
I’ll wrap this title and span within another div with the className attribute and styles
cartInfo, now I’ll select this class name cartInfo and apply following style, display
flex, flex direction column, padding left and right sides of the element with 0 0.9rem.
Select itemQuantity class name and give it background color.
Now I’ll target button elements that are descendants of element with classname itemQuantity,
so set background color to transparent, border to none and color white, font size 16px and
font weight bold. Let’s see result. It’s looking good.
In the Cart component, let’s handle actions like removing an item from the cart or clearing
the entire cart. Dispatch these actions to the cartReducer. I am going to declare three function handlers
inside the Cart component: one each for the Clear Cart Item, Increasing Quantity, and
Decreasing Quantity. Define function called handleClearItem using
arrow function syntax. Define another function called handleIncrements
that takes an itemId as its argument, responsible for incrementing the quantity of a specific
item in the cart. This itemId likely represents the unique identifier of the item you want
to increment in the cart. const handleIncrements = (itemId) => {}
Let’s declare another new function called handleDecrements and also takes an argument
id, it’ll identity which drink item you want to decrement in cart.
Now within handleclearItem handler function pass contextItems object and call clearItem()
function on it. Here contextItems object provides access to
clearItem function, which we already declared inside context provider. Now you can define
onClick event on button and pass handler function inside curly braces. So now when clear all
button is clicked it’s going to dispatch action to cartReducer to clear items from
cart. Now I want to call increment function when
plus button is clicked in cart and pass it to context api.
Inside handleIncrements function, first I’m going to use find method to search for a drink
item in contextItems. Let’s define a new const named item and
specify contextItems.items.find() and inside pass item as an argument and looks for an
item id that match provided itemId Now checks if an item with the specified itemId
was found in the cart. If such an item exists, the code block inside the if statement will
execute. So create a new updatedItem object and use
spread operator …item to copy all properties of the found item, now set quantity property
of this new item to 1, effectively incrementing the quantity to 1. In next line, call addItem
function from contextItems object and pass updatedItem inside parenthesis; it’s responsible
for adding updated item with quantity 1. We already implemented logic for removal or
decrementation within context, so here inside handleDecrement function call removeItem function
from contextItems object … and pass id as an argument.
This implies that removeItem is responsible for removing items from the cart based on
their id. Now for button to work I’ll pass these handler
function to nested component CartItem. Pass onAdd property and pass handler function because
handleIncrements function receiving an argument itemId and because we can’t pass it directly
like this, we've got to pass it as an anonymous function and pass correct item.id to update
cart accordingly. Just like that set another event onRemove and call anonymous function
handleDecrement with item.id. In CartItem component on decreasing button pass onClick
event with property onRemove and on increasing button pass onClick event with property onAdd.
Let’s save it all and let’s see if it works, click plus button you see number is
increasing and click minus button and qunaity of item decreasing and if it’s one item
it should remove this drink from cart press button and it does remove. Great! Now I want to update price based on quantity,
I want to dynamically calculate price, so let us go to the CartItem component. Inside
of this functional component, I am going to add a new const named price, and because I
want to display price as a string with a dollar sign, I will use template tileral use two
dollar signs, one for the string and one for running javascript expression. Now pass drink
price and use multply operator and it’s going to be calculated by its quantity that
we are receiving from Cart component. In JSX, instead of props.price pass new created variable
price. If you go to browser and check the price is
updating by its quantity dynamically if I add or decreases an item.
The final step is to get the subtotal and display it in the cart.
In order to accomplish this inside cart component, I’ll create a new const subtotal, that’s
gonna hold a value as a string. Specify template literal, and then we’ll execute the expression.
Let’s retrieve the total price of all items from the cartContext object that holds information
about drinks in a shopping cart, it’s a floating-point number, now apply javascript
method toFixed with value 2, used to round the total to exactly two decimal places. So
pass the subtotal const inside span instead of 0 save it all and you’ll see total is
also updating as well. Now I want to render this cart container as
modal I want to hide this cart and only let it appear when cart button is clicked. And
I’ll use another react key feature which is Portal. Portal used to allow shopping cart to appear
as an overlay or separate container on top of other components. So let’s go to vscode and inside layout
create new component called Modal.js and create another file named Modal.module.css. I created separated video on React Portals,
so you can watch it from there. Now let’s use rafce short key to create
anonymous function, remove this line we don’t this anymore and at top of this functional
component import Fragment which is built-in component.
In JSX, you can use <Fragment> element, without actually rendering any additional HTML element.
Now Import module with styles object and in next line I’ll import createPortal method
from react-dom. In JSX, I’ll use curly braces because it’s
Javascript method, and specify createPortal() to it. I’ll create portal node to render my cart
modal. So let’s go to index.js file which is located inside public folder. Inside this
file scroll to root div element and before this rood id I’ll create another DOM element
div with id overlay-root and instead overlay-root you can name it as you preferred.
Now let’s go back to Modal component, and here I’ll render Cart as a modal dialog
inside createPortal method. createPortal() method accepts two argument
jsx and DOM element where component will be rendered. I’ll use div parent element with className
attribute and set it to style.cartModal which is yet to define into module. Inside this
div element provide Cart component, make sure you import it.
Now after comma pass Dom element using document.getElementById() and pass id overlay-root inside which we defined
earlier. That’s it this is a common technique for creating modal dialog in React application.
Now inside App component instead of Cart element I’ll replace it with Modal component becuase
We are rendering cart inside modal. So in JSX, above header element pass Modal as nested
component also import it at the top. Now let’s give it style so it should appear
as modal. Inside module select cartModal class as selector. Set position to fixed, ensures that the modal
is fixed in place and doesn't move as the user scrolls the page
top 10vh viwport height it sets the distance of the modal from the top of the viewport
right 0 positions the modal against the right edge of the viewport, z-index 30, height 100vh,
max width 100%, I’ll also give it animation called slide-out
duration of 500ms and use forwards keyword. Now let’s define keyframes rules or animation
named slide-out and give it starting point from and ending point to.
From the beginning, set transform to translateX 100%, effectively hiding it to right of viewport.
At the end of animation set transform to translateX 0%, translated back to normal position. Let’s
give it height to 100%, now it’s looking good and refresh it, it’s animating well. Now I want to add toggle functionality to
this modal, show and hide on button clicked. But cart button is located inside Header component
and Header components nested inside App so we’ll call useState hook to our root component,
that also called lifting state up. So at top import useState from react library
and let’s define pattern. Use const keyword and inside square brackets
set variable name toggleCart and updating function setToggleCart, call useState hook
and specify initial boolean value false. I’ll use toggleCart state variable for conditional
rendering, inside JSX wrap curly braces around Modal specify variable toggleCart and use
AND operator, save it all and let’s see cart modal disappear because initial value
is false, if I set it to true cart modal is appeared.
Now let’s declare arrow function with const keyword and name it toggleCartHandler for
toggling visibility of cart modal, and inside this function pass setToggleCart to update
state variable now pass arrow function which takes prevState as an argument and negates
previous state using not logical operator. If the previous state was true, it becomes
false, and if it was false, it becomes true. In other words, it toggles the state from
its current value. Now I’ll pass this handler function to Header,
declare onToggle event, it’s not built-in event so you can name it as you prefer and
now pass handler function toggleCartHandler inside.
We’ve cart button nested inside header so let’s go to Header component and here you
can props keyword or directly use onToggle within curlybraces. Now on CartButton define
onClick event and set it to onToggle. Go to CartButton component and there you pass props,
and on the button define onClick event with props.onClick. Passing props through multiple
components is called props drilling but in my If the previous state was true, it becomes
false, and if it was false, it becomes true. In other words, it toggles the state from
its current value. Now I’ll pass this handler function to Header,
declare onToggle event, it’s not built-in event so you can name it as you prefer and
now pass handler function toggleCartHandler inside.
case, passing through just two components is acceptable. Now let’s go to browser and check you’ll
see cart modal disappear and now click on this cart button it slides out from right.
You can click multiple times to check if it’s working properly.
We’ve this close button inside Cart So I also want it to work when I click on it cart
should close. I’ll declare another function called closeCartHandler
using const keyword to close cart modal, and inside this function call setToggleCart and
set it to false, As a result, if the toggleCart state variable is true indicating that the
cart modal is open, it will now be set to false, and the cart modal will be hidden.
There is a close button inside Cart so on Modal, so I’m going to define onClose event
and inside curly braces, pass closeCartHandler function. Now go to modal and pass props inside
parenthesis, define onClose property on Cart and pass props.onClose inside. Now go to Cart
component pass props here as well inside parenthesis as well and on button declare onClick built-in
event and pass property props.onClose, we were passing function handler from parent
which is why we were defining our own event but on button we need to use onClick event
to work it properly. Now we can check if button is working. In
browser, click on close button, it disappeared. It’s good but the last thing I want when
this cart modal appears is to draw shadow behind this cart modal to make it stand out.
Let’s go to Modal component, I’m going to declare another createPortal before cart
modal method self close div with className attribute
and set style.backdrop after comma document.getElementById with id overlay-root. I also want to close modal when click outside
so on div pass onClick event and pass props.onClose property receiving from App component. Now
let’s specify style to create translucent background. In module select class backdrop set position
to fixed, top 0, left 0, width 100%, height 100%, z-index 20 typically places it above
most other page content but below the modal itself, now set background color with a semi-transparent
black color with rgb and set it to 0.5 These styles create a fixed-position, full-viewport
backdrop with a semi-transparent black color to dim the background content when a modal
is displayed. Let’s see click on button modal will now
slide in and become visible on the screen and if you click on backdrop or outside of
modal, it will trigger the onClose function to close the modal. So this is what I promised you that we’ll
build today, Our primary motivation for developing this simple application was that we could
utilize React's key features. I hope you learned how to put together and build things up. I’ll
end this here, if you’ve any questions regarding this, comment below and if you enjoyed this
video please like and share and follow me on instagram. I’ll see you in next one.