E-commerce photo store app
React, React Router, React Redux, Unsplash
*Does not sell products*
View Demo · Report Bug · Request Feature
Art Prints is an e-commerce application, where users can search images and add them to a shopping cart.
This application is built to mimic the user experience of an e-commerce website. Features include keyword search, product customization (size and quantity), shopping cart review/edit/delete, search history, product reviews, similar product tags, and more.
This is a client-side single-page application (SPA) built using React for the client interface capabilities, React Redux for centralized state management, React Router for client-side routing, and Unsplash API for image search.
There is no checkout process implemented in the application.
Open the live demo here, or clone the repository, (node and npm are required).
Install npm packages
npm install
Start Server
npm start
Navigate to localhost in your browser
localhost:3000
The primary purpose for the application was to plan and practice building a modern Single Page Application (SPA) using several modern development libraries including:
- React - front end user interface and reusable components
- React Router - for client side routing and navigation of pages
- Redux - global state management
A few secondary/add-on objectives included:
- Working with APIs
- Responsive layout
- Optimization
- Code Splitting, using React Suspense and Lazy
- Image Optimization, via file type, loading priority, and screen size
- Testing with React Testing Library
Search
Search by term is a key feature of any e-commerce application. The user must be able to describe the product they are looking for and search is the most common method of doing so. In the UI this is the text input field where the user can type in their desired keyword.
Tags are a feature that allows the user to browse products that have been identified with a keyword to be similar or adjacent to their initial search. This can be found at the top of the results page and in the product details.
History is a listing of all the unique searches and tag clicks that have occured in the current session. It's a useful feature that allows users to browse and return at later time. This feature can be found in the header of the application next to the search input field.
TLDR - higher order components were utiziled to give state and event handlers to the components.
The Search, Tags, and History components share state types (e.g search term) and event handlers, therefore a higher order component (HOC) can be utilized. The HOC wraps around the original component and passes the state and event handlers down through props to the original component. This eliminates duplicate code written in each of these components and allows for the HOC to be the single location which defines the pieces of state and event handlers. When the component is called to render, the component first passes through the HOC to return a new component with the required state and event handlers now available.
The execution of the search for all three features is very similar thanks to the HOC. When the user submits a term via the search input field or clicks a tag or history item, the submit type (e.g. 'search', 'tags', 'history') and the term is passed to the handleSubmit callback where the term will be sent to a data helper called 'search' to submit a request to Unsplash for results of the term. The response will be processed through additional helpers to generate products and tags for the results, then create a new results object for the results. After which the results are dispatched to Redux and the user is redirected to the results page where the results will render.
Tags and History also share a very similar approach of mapping over an array to return React Router Link components and could likely be refactored to a single component, as after all both components are simply producing a list of links.
Cart
The ability for the user to review the items they have added to their cart is essential for any e-commerce application. Users want to be able to confirm that the items in their cart are what they are buying.In this shopping cart, the user can see their selected product, the customization they have applied (i.e. size and quantity), and the price for the product given their selections. The user may also click on the image or the product title and be redirected back to the product page.
Additional useful features of a shopping cart is the ability to edit or delete the product the user has added to their cart.
These features have been added such that the user may simply click the delete button listed in each cart item and the application state will update the shopping cart and re-render showing the updated cart items.
Responsive Design
A responsive design is an essential feature in any modern web application and greatly improves the user experience. A good responsive design is intuitive and helps the user interact with the application to achieve their purpose for using the application. This application is designed to work on a mobile, tablet, or desktop screensizes.Pagination
Anytime a user will be browsing a large listing of items, it's always a good idea to implement pagination. Doing so breaks down the information in a structured way, thus providing a more meaningful and easily digestible user experience.Pagination is implemented in two components of the application, the first in the results component, where the user can click the 'Load More' button to show more search results. The other component that features pagination is in the product reviews, where if there are more reviews than fits the page limit, the pagination will display at the bottom of the reviews as a numbered list of pages with forward and back arrows for navigation.
Modals
A modal is used to draw the user's attention to an action or to highlight something they have interacted with. In this application modals are used to display the search history component and view the product (image) in isolation when clicked on.To implement modals, React's createPortal()
method was employed to render the component outside the flow of the component where is it called.
Data Generators
This application doesn't actually sell real products, it uses the Unspash API which hosts a huge collection of high-quality photographs from photographers world-wide for free. Therefore, "products" had to be generated, to achieve this, functions were created to transform the search results into fake products that could be rendered and interacted with by the user.The response from the Unsplash API returns an array of objects, each of which include various properties about the image, e.g. image urls, owner information, and other data necessary for usage of the results. However, for this application more data was needed to flesh out products, such as a base amount for pricing, likes, reviews, and product tags.
When the response from Unsplash is received, the results are passed into a function called buildProducts()
which maps over the results array and returns a new array of product objects with the following properties:
{
id: product.id,
description: product.description || 'none',
alt_description: product.alt_description || 'none',
image_urls: product.urls || 'none',
tags: product.tags || [],
base_amt: (Math.random() * 10 + 6).toFixed(2),
width: product.width || 2500,
height: product.height || 1600,
orientation: product.width > product.height ? 'landscape' : 'portrait',
quantity_available: stockQuantity(),
owner: product.user || 'none',
likes: (Math.random() * 1000).toFixed(),
created_at: product.created_at,
review_count: Math.round(Math.random() * 1000)
}
In addition to generating products, another function buildReviews()
generates fake product reviews using the Faker API, here is an example of a single review object returned by the function:
{
product_id: product.id,
review_id: uuidv4(),
comment: {
title: faker.lorem.words(),
comments: faker.lorem.paragraph(),
},
date: date.toString(),
user: faker.internet.userName(),
rating: Math.ceil(Math.random() * 5),
}
All the products and reviews are added to their respective arrays then dispatched to be updated in the application state.
The basic usage of the application is as follows:
- Upon load of the application the user may navigate to the search bar at the top and enter a search term for a desired search result, e.g. "nature", and click the 'Search' button or hit enter. The user may also select the history icon, to view suggested searches.
- browse the search results
- select 'Load More' at the bottom of the results listing to fetch more results
- select a tag from above the results for similar search results
NOTE: There is no checkout feature built into this application.
Distributed under the MIT License. See LICENSE.txt
for more information.