Building an E-Commerce Application with Products, Cart, and Orders in React Native
Course: React Native Training
Topic: Building an E-Commerce Application with Products, Cart, and Orders
Course Link: React Native Training – JustAcademy
Demo Link: Register for a Course Demo
Website: JustAcademy
1. Introduction to E-Commerce Application Development
An e-commerce application allows users to browse products, view product details, add items to a shopping cart, manage quantities, place orders, and track their purchases. React Native makes it possible to build such mobile applications for Android and iOS using a shared JavaScript and React-based codebase.
Building an e-commerce application is a practical way to learn mobile application architecture, component development, navigation, API integration, state management, data persistence, form validation, and secure backend communication.
1.1 Main Features of an E-Commerce Application
- Product listing: Display products with images, names, prices, and ratings.
- Product details: Show descriptions, available sizes, colors, specifications, and stock information.
- Shopping cart: Add products, remove items, and update quantities.
- Price calculation: Calculate subtotal, discounts, delivery charges, taxes, and total payable amount.
- Checkout: Collect delivery details and allow users to review their purchases.
- Order placement: Submit the checkout request to a backend service.
- Order history: Display previous orders and their statuses.
- Authentication: Support customer registration and login where required.
- Payment integration: Connect to a secure payment provider when online payments are needed.
1.2 Learning Objectives
- Understand the architecture of an e-commerce mobile application.
- Create reusable product, cart, checkout, and order components.
- Fetch product data from a REST API.
- Manage cart state and calculate order totals.
- Persist appropriate cart data across application restarts.
- Validate checkout forms and handle errors.
- Submit orders through a backend API.
- Understand important security, testing, and performance considerations.
2. Understanding the Application Architecture
A well-structured e-commerce application separates the user interface, business logic, data access, and backend responsibilities. This makes the code easier to maintain, test, and extend.
2.1 Frontend Responsibilities
- Render product listings and product details.
- Respond to user interactions such as adding or removing products.
- Display cart contents and calculated totals.
- Collect checkout information and validate required fields.
- Show loading indicators, success messages, and error states.
- Send requests to backend APIs and display their responses.
2.2 Backend Responsibilities
- Store products, categories, prices, and stock information.
- Authenticate users and authorize protected operations.
- Validate product availability and prices.
- Calculate authoritative order totals.
- Create orders and manage order statuses.
- Integrate with payment providers and process payment notifications.
- Maintain order history and customer records.
2.3 Typical Application Flow
- The application requests product data from a backend API.
- The user browses the product listing.
- The user opens a product detail screen.
- The user adds one or more products to the cart.
- The application calculates and displays the cart subtotal.
- The user reviews the cart and proceeds to checkout.
- The application validates the delivery details.
- The backend verifies stock and pricing and creates the order.
- If payment is required, the payment provider processes it through a secure integration.
- The application displays the order confirmation and later retrieves order history from the backend.
3. Setting Up the React Native Project
Start with a React Native project created using the React Native tooling or Expo. The exact setup depends on the development environment and the project requirements.
3.1 Create an Expo Project
One common way to start a React Native project is to use Expo:
npx create-expo-app EcommerceApp
cd EcommerceApp
npx expo start
Follow the current Expo setup instructions if your development environment requires additional configuration.
3.2 Install Navigation and Storage Dependencies
A typical project may use React Navigation for moving between screens and AsyncStorage for simple local persistence. Install packages according to their current official compatibility instructions.
npm install @react-navigation/native
npm install @react-navigation/native-stack
npm install @react-native-async-storage/async-storage
Expo projects may require compatible native dependencies for navigation. Follow the setup instructions for the chosen navigation and Expo versions.
3.3 Suggested Project Structure
EcommerceApp/
├── src/
│ ├── components/
│ │ ├── ProductCard.js
│ │ ├── CartItem.js
│ │ └── PrimaryButton.js
│ ├── screens/
│ │ ├── ProductListScreen.js
│ │ ├── ProductDetailsScreen.js
│ │ ├── CartScreen.js
│ │ ├── CheckoutScreen.js
│ │ ├── OrderConfirmationScreen.js
│ │ └── OrderHistoryScreen.js
│ ├── context/
│ │ └── CartContext.js
│ ├── services/
│ │ ├── productService.js
│ │ └── orderService.js
│ ├── utils/
│ │ └── pricing.js
│ └── navigation/
│ └── AppNavigator.js
├── App.js
└── package.json
This structure is a suggested starting point. Larger applications may use feature-based folders, dedicated API clients, a state management library, and separate models or type definitions.
4. Designing the Product Listing Screen
The product listing screen is usually the first shopping screen. It presents available products in a format that allows users to quickly compare items and select products they want to purchase.
4.1 Product Data Model
A product object may contain fields such as the following:
const product = {
id: "p101",
name: "Wireless Headphones",
description: "Comfortable wireless headphones",
price: 2499,
image: "https://example.com/headphones.jpg",
category: "Electronics",
stock: 15,
rating: 4.5
};
The example uses sample data. A production application should retrieve authoritative product information from its backend.
4.2 Create a Reusable Product Card
A reusable product card keeps the product interface consistent and reduces duplicated code.
import React from "react";
import {
View,
Text,
Image,
Pressable,
StyleSheet
} from "react-native";
export default function ProductCard({ product, onPress }) {
return (
<Pressable
style={styles.card}
onPress={onPress}
accessibilityRole="button"
accessibilityLabel={`View ${product.name}`}
>
<Image
source={{ uri: product.image }}
style={styles.image}
resizeMode="cover"
/>
<Text style={styles.name}>{product.name}</Text>
<Text style={styles.price}>₹{product.price.toFixed(2)}</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
card: {
flex: 1,
margin: 8,
padding: 12,
borderRadius: 10,
backgroundColor: "#FFFFFF",
elevation: 2
},
image: {
width: "100%",
height: 140,
borderRadius: 8
},
name: {
fontSize: 16,
fontWeight: "600",
marginTop: 8
},
price: {
fontSize: 15,
marginTop: 5
}
});
4.3 Display Multiple Products
Use FlatList to display product collections efficiently. It renders list items as needed and supports large lists better than placing every item inside a ScrollView.
import React from "react";
import { FlatList, View } from "react-native";
import ProductCard from "../components/ProductCard";
export default function ProductListScreen({ navigation }) {
const products = [
{
id: "p101",
name: "Wireless Headphones",
price: 2499,
image: "https://example.com/headphones.jpg"
},
{
id: "p102",
name: "Smart Watch",
price: 3999,
image: "https://example.com/watch.jpg"
}
];
return (
<View style={{ flex: 1 }}>
<FlatList
data={products}
numColumns={2}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<ProductCard
product={item}
onPress={() =>
navigation.navigate("ProductDetails", {
productId: item.id
})
}
/>
)}
/>
</View>
);
}
For a real application, replace the hard-coded array with data fetched from a product service. Include loading, empty, and error states, and use stable unique product identifiers.
5. Fetching Products from a REST API
A REST API allows a mobile application to request product information from a server. Common endpoints include product listing, product details, categories, inventory, and search.
5.1 Example API Endpoints
| Method |
Endpoint |
Purpose |
| GET |
/products |
Retrieve a product list. |
| GET |
/products/:id |
Retrieve one product. |
| GET |
/categories |
Retrieve product categories. |
| GET |
/cart |
Retrieve a server-managed cart, if supported. |
| POST |
/orders |
Create an order. |
| GET |
/orders |
Retrieve the authenticated customer's orders. |
| GET |
/orders/:id |
Retrieve a specific order. |
These are illustrative routes. The actual paths, request bodies, authentication requirements, and response formats depend on your backend API.
5.2 Create a Product Service
const API_BASE_URL = "https://api.example.com";
export async function getProducts() {
const response = await fetch(`${API_BASE_URL}/products`);
if (!response.ok) {
throw new Error("Unable to load products.");
}
const data = await response.json();
return data;
}
export async function getProductById(productId) {
const response = await fetch(
`${API_BASE_URL}/products/${encodeURIComponent(productId)}`
);
if (!response.ok) {
throw new Error("Unable to load product details.");
}
return response.json();
}
Use HTTPS for network requests. The API host above is a placeholder and must be replaced with your backend address.
5.3 Handle Loading and Error States
API calls can fail because of network problems, server errors, invalid responses, or timeouts. A product screen should clearly communicate its current state.
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
let active = true;
async function loadProducts() {
try {
setLoading(true);
setError("");
const result = await getProducts();
if (active) {
setProducts(result);
}
} catch (err) {
if (active) {
setError(err.message || "Something went wrong.");
}
} finally {
if (active) {
setLoading(false);
}
}
}
loadProducts();
return () => {
active = false;
};
}, []);
In the screen's JSX, render a loading indicator while the request is running, a retry option when an error occurs, an empty-state message when there are no products, and the list when data is available. For more complex requests, use AbortController or the data-fetching library's cancellation features to prevent unnecessary work.
6. Creating the Product Details Screen
The product details screen provides more information than the product listing. It should help the user decide whether to add an item to the cart.
6.1 Important Product Details
- Product name and images.
- Current price and applicable discount information.
- Product description and specifications.
- Available sizes, colors, or other variants.
- Stock availability.
- Quantity selection, where applicable.
- An Add to Cart action.
6.2 Retrieve a Product by ID
Pass a product identifier through navigation rather than relying on a potentially outdated product object. Retrieve current details when necessary.
function ProductDetailsScreen({ route }) {
const { productId } = route.params;
const [product, setProduct] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
let active = true;
getProductById(productId)
.then((data) => {
if (active) setProduct(data);
})
.catch((err) => {
if (active) {
setError(err.message || "Could not load product.");
}
})
.finally(() => {
if (active) setLoading(false);
});
return () => {
active = false;
};
}, [productId]);
if (loading) {
return <Text>Loading product...</Text>;
}
if (error) {
return <Text>{error}</Text>;
}
if (!product) {
return <Text>Product not found.</Text>;
}
return (
<View>
<Text>{product.name}</Text>
<Text>₹{Number(product.price).toFixed(2)}</Text>
<Text>{product.description}</Text>
</View>
);
}
This example illustrates data retrieval and screen states. Import the required React hooks and React Native components, and connect the Add to Cart button to the cart state provider described below.
7. Understanding the Shopping Cart
The shopping cart holds the items a customer intends to purchase. It needs to support adding products, updating quantities, removing items, clearing the cart, and calculating totals.
7.1 Cart Item Structure
const cartItem = {
productId: "p101",
name: "Wireless Headphones",
price: 2499,
image: "https://example.com/headphones.jpg",
quantity: 2
};
In a production application, the backend should validate the current price and availability. A client-side price field is useful for display, but it is not authoritative for order creation.
7.2 Add a Product to the Cart
When a user adds a product, check whether the product is already present in the cart. If it is, increase its quantity; otherwise, add a new cart item.
function addToCart(cartItems, product) {
const existingItem = cartItems.find(
(item) => item.productId === product.id
);
if (existingItem) {
return cartItems.map((item) =>
item.productId === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
}
return [
...cartItems,
{
productId: product.id,
name: product.name,
price: product.price,
image: product.image,
quantity: 1
}
];
}
This is an immutable update: it returns a new array and new objects for modified items instead of mutating the original state. Add stock and quantity-limit checks where the business rules require them.
7.3 Increase or Decrease Quantity
function updateQuantity(cartItems, productId, quantity) {
if (!Number.isInteger(quantity) || quantity < 1) {
return cartItems.filter(
(item) => item.productId !== productId
);
}
return cartItems.map((item) =>
item.productId === productId
? { ...item, quantity }
: item
);
}
In this example, quantities below one remove the item. Another valid design is to disable the decrease button when quantity equals one and provide a separate Remove button. Ensure quantities cannot exceed available stock.
7.4 Remove an Item
function removeFromCart(cartItems, productId) {
return cartItems.filter(
(item) => item.productId !== productId
);
}
7.5 Clear the Cart
function clearCart() {
setCartItems([]);
}
Clear the cart only after the application has received a successful order-creation response or after the user explicitly chooses to clear it. If payment is pending, preserve the appropriate order and checkout state.
8. Managing Cart State with React Context
React Context can share cart data and cart operations across screens without passing props through every intermediate component. For larger applications, Redux Toolkit, Zustand, or another state management solution may be appropriate.
8.1 Create CartContext
import React, {
createContext,
useContext,
useMemo,
useState
} from "react";
const CartContext = createContext(null);
export function CartProvider({ children }) {
const [cartItems, setCartItems] = useState([]);
function addToCart(product) {
setCartItems((currentItems) => {
const existing = currentItems.find(
(item) => item.productId === product.id
);
if (existing) {
return currentItems.map((item) =>
item.productId === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
}
return [
...currentItems,
{
productId: product.id,
name: product.name,
price: product.price,
image: product.image,
quantity: 1
}
];
});
}
function removeFromCart(productId) {
setCartItems((currentItems) =>
currentItems.filter(
(item) => item.productId !== productId
)
);
}
function changeQuantity(productId, quantity) {
if (!Number.isInteger(quantity) || quantity < 1) {
removeFromCart(productId);
return;
}
setCartItems((currentItems) =>
currentItems.map((item) =>
item.productId === productId
? { ...item, quantity }
: item
)
);
}
function clearCart() {
setCartItems([]);
}
const itemCount = cartItems.reduce(
(total, item) => total + item.quantity,
0
);
const subtotal = cartItems.reduce(
(total, item) => total + item.price * item.quantity,
0
);
const value = useMemo(
() => ({
cartItems,
itemCount,
subtotal,
addToCart,
removeFromCart,
changeQuantity,
clearCart
}),
[cartItems, itemCount, subtotal]
);
return (
<CartContext.Provider value={value}>
{children}
</CartContext.Provider>
);
}
export function useCart() {
const context = useContext(CartContext);
if (!context) {
throw new Error("useCart must be used inside CartProvider");
}
return context;
}
For a larger application, consider moving cart operations outside the provider component or using a reducer to keep update logic organized. Add stock checks and server synchronization as appropriate. If the cart must persist, load and save it through a dedicated persistence layer.
8.2 Wrap the Application with CartProvider
import React from "react";
import { CartProvider } from "./src/context/CartContext";
import AppNavigator from "./src/navigation/AppNavigator";
export default function App() {
return (
<CartProvider>
<AppNavigator />
</CartProvider>
);
}
8.3 Use the Cart Context in a Product Screen
import React from "react";
import { Button, Text, View } from "react-native";
import { useCart } from "../context/CartContext";
export default function ProductActions({ product }) {
const { addToCart, itemCount } = useCart();
return (
<View>
<Text>Items in cart: {itemCount}</Text>
<Button
title="Add to Cart"
onPress={() => addToCart(product)}
/>
</View>
);
}
Keep the cart badge synchronized with the cart context so that screens do not maintain separate, conflicting copies of the cart count.
9. Building the Cart Screen
The cart screen allows users to review selected products before checkout. It should clearly show item quantities, item prices, line totals, subtotal, and the next available action.
9.1 Cart Screen Requirements
- Display each product's name, image, and current displayed price.
- Display quantity controls.
- Show a Remove action for each item.
- Calculate line totals and the cart subtotal.
- Show an empty-cart state when there are no items.
- Provide a Continue Shopping action.
- Provide a Proceed to Checkout action when the cart is not empty.
9.2 Calculate the Subtotal
const subtotal = cartItems.reduce(
(total, item) => total + item.price * item.quantity,
0
);
The subtotal is the sum of each item's displayed price multiplied by its quantity. It does not necessarily include shipping, taxes, discounts, or other checkout charges.
9.3 Example Cart Item Component
import React from "react";
import { View, Text, Button, Image } from "react-native";
export default function CartItem({
item,
onIncrease,
onDecrease,
onRemove
}) {
return (
<View>
<Image
source={{ uri: item.image }}
style={{ width: 80, height: 80 }}
/>
<Text>{item.name}</Text>
<Text>₹{Number(item.price).toFixed(2)} each</Text>
<Text>Quantity: {item.quantity}</Text>
<Text>
Line total: ₹{(item.price * item.quantity).toFixed(2)}
</Text>
<Button title="-" onPress={onDecrease} />
<Button title="+" onPress={onIncrease} />
<Button title="Remove" onPress={onRemove} />
</View>
);
}
Connect the callbacks to the cart context. In a polished interface, disable quantity controls at valid limits, add accessible labels, and ask for confirmation before destructive actions if appropriate.
10. Pricing, Discounts, Shipping, and Taxes
Pricing logic is important because customers need to understand the amount they will pay before submitting an order.
10.1 Common Pricing Components
| Component |
Description |
| Subtotal |
Total of item prices multiplied by quantities. |
| Discount |
An eligible reduction based on a coupon or promotion. |
| Shipping |
Delivery charge calculated from applicable delivery rules. |
| Tax |
Tax calculated according to applicable rules and location. |
| Total |
Final amount due after discounts and applicable charges. |
10.2 Example Calculation
const subtotal = 2500;
const discount = 200;
const shipping = 80;
const tax = 0; // Example only; use the applicable tax calculation.
const total = subtotal - discount + shipping + tax;
In this example, the total is ₹2,380. The tax value is set to zero only to demonstrate the formula; a real application must calculate taxes according to its applicable rules.
10.3 Centralize Pricing Logic
export function calculateSubtotal(items) {
return items.reduce((sum, item) => {
const price = Number(item.price);
const quantity = Number(item.quantity);
if (
!Number.isFinite(price) ||
!Number.isInteger(quantity) ||
price < 0 ||
quantity < 1
) {
return sum;
}
return sum + price * quantity;
}, 0);
}
export function calculateTotal({
subtotal,
discount = 0,
shipping = 0,
tax = 0
}) {
return Math.max(0, subtotal - discount + shipping + tax);
}
This utility demonstrates basic client-side calculations. A production system should validate every pricing input and calculate the final order amount on the server. For currencies that require exact decimal arithmetic, use a reliable money representation such as integer minor units or a decimal arithmetic library instead of relying on floating-point values for financial calculations.
11. Persisting the Cart Between App Sessions
Without persistence, an in-memory cart is lost when the application process is terminated. AsyncStorage can be used for simple local cart persistence. It is asynchronous and stores data as strings.
11.1 Save Cart Data
import AsyncStorage from
"@react-native-async-storage/async-storage";
const CART_STORAGE_KEY = "shopping_cart";
export async function saveCart(cartItems) {
try {
await AsyncStorage.setItem(
CART_STORAGE_KEY,
JSON.stringify(cartItems)
);
} catch (error) {
console.error("Could not save cart", error);
}
}
11.2 Load Cart Data
export async function loadCart() {
try {
const storedValue = await AsyncStorage.getItem(
CART_STORAGE_KEY
);
if (!storedValue) {
return [];
}
const parsedValue = JSON.parse(storedValue);
return Array.isArray(parsedValue) ? parsedValue : [];
} catch (error) {
console.error("Could not load cart", error);
return [];
}
}
11.3 Integrate Persistence with Cart State
Load the cart when the provider initializes and save changes when the cart changes. Prevent an initial empty state from overwriting previously saved data before loading finishes.
const [cartItems, setCartItems] = useState([]);
const [cartLoaded, setCartLoaded] = useState(false);
useEffect(() => {
let active = true;
loadCart()
.then((items) => {
if (active) setCartItems(items);
})
.finally(() => {
if (active) setCartLoaded(true);
});
return () => {
active = false;
};
}, []);
useEffect(() => {
if (cartLoaded) {
saveCart(cartItems);
}
}, [cartItems, cartLoaded]);
This is an illustrative integration snippet and belongs inside the cart provider with the required imports. Production code should also validate the stored structure, handle storage failures, and avoid overwriting newer data when multiple updates happen quickly. Consider server-managed carts for authenticated users who shop across multiple devices.
12. Building the Checkout Screen
The checkout screen collects the information needed to fulfill an order. The exact fields depend on the business model, delivery method, region, and payment options.
12.1 Common Checkout Fields
- Customer name.
- Contact number or email, where required.
- Delivery address.
- City, region, and postal code.
- Delivery instructions, if supported.
- Selected delivery option.
- Selected payment method.
- Order summary and final displayed total.
12.2 Validate Checkout Inputs
Validation should check that required fields are present, values are within expected limits, and the cart contains valid items. Client-side validation improves usability, but the backend must repeat validation because a client can be modified or bypassed.
export function validateCheckout(values) {
const errors = {};
if (!values.name?.trim()) {
errors.name = "Name is required.";
}
if (!values.phone?.trim()) {
errors.phone = "Contact number is required.";
}
if (!values.address?.trim()) {
errors.address = "Address is required.";
}
if (!values.city?.trim()) {
errors.city = "City is required.";
}
if (!values.postalCode?.trim()) {
errors.postalCode = "Postal code is required.";
}
return errors;
}
Additional checks may be necessary for phone formats, postal codes, address length, supported delivery regions, and country-specific requirements. Do not rely on a single regular expression as proof that a contact number or address is valid.
12.3 Prevent Duplicate Submissions
Disable the Place Order button while a request is being processed. The backend should also support idempotency or another duplicate-request protection mechanism so that retries do not accidentally create multiple orders.
const [submitting, setSubmitting] = useState(false);
async function handlePlaceOrder() {
if (submitting) return;
setSubmitting(true);
try {
// Validate fields and submit the order request.
} catch (error) {
// Display a clear error message.
} finally {
setSubmitting(false);
}
}
13. Creating an Order Through a Backend API
Placing an order is a critical operation. The mobile application sends the customer's selected items and necessary checkout details to the backend. The backend validates the request, retrieves current product prices, checks stock, calculates the final total, and creates the order according to its business rules.
13.1 Example Order Request
{
"items": [
{
"productId": "p101",
"quantity": 2
},
{
"productId": "p102",
"quantity": 1
}
],
"shippingAddress": {
"name": "Sample Customer",
"phone": "9999999999",
"addressLine": "Example Road",
"city": "Mumbai",
"postalCode": "400001"
},
"paymentMethod": "cash_on_delivery"
}
This is illustrative JSON. A real API may require a different structure. The client should generally send product identifiers and quantities rather than treating client-supplied prices or totals as authoritative.
13.2 Create an Order Service
const API_BASE_URL = "https://api.example.com";
export async function createOrder(orderPayload, accessToken) {
const response = await fetch(`${API_BASE_URL}/orders`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`
},
body: JSON.stringify(orderPayload)
});
const result = await response.json().catch(() => null);
if (!response.ok) {
throw new Error(
result?.message || "Unable to place your order."
);
}
return result;
}
Use the authentication token only when the API requires it, and obtain it through the application's approved authentication flow. Do not hard-code real user tokens in source code. Handle expired sessions and unauthorized responses appropriately.
13.3 Order Submission Workflow
- Confirm the cart contains at least one item.
- Validate checkout fields.
- Show a summary of the selected items and delivery details.
- Submit product identifiers, quantities, and required checkout details.
- Allow the backend to validate stock and calculate the authoritative total.
- Process the response and display the resulting order information.
- Clear or reconcile the cart only when the order workflow reaches the appropriate confirmed state.
14. Understanding Order Statuses
Orders commonly move through multiple statuses. The available statuses depend on the backend and the business workflow.
| Status |
Meaning |
| Pending |
The order has been received but requires further processing. |
| Confirmed |
The order has passed the relevant confirmation checks. |
| Processing |
The order is being prepared. |
| Shipped |
The order has been handed over for delivery. |
| Delivered |
The order has been marked as delivered. |
| Cancelled |
The order has been cancelled according to the applicable rules. |
| Payment Failed |
A payment attempt did not complete successfully. |
| Refunded |
A refund has been processed or recorded. |
Do not assume that a successful HTTP response means payment has been completed. Order creation and payment confirmation can be separate operations. Use the backend's defined order and payment statuses.
15. Building an Order Confirmation Screen
The order confirmation screen reassures the customer that the request was received and provides important order details.
15.1 Information to Display
- Order reference or order number.
- Order creation date and time, when supplied by the backend.
- Items and quantities.
- Confirmed total and currency.
- Delivery address or selected delivery method.
- Current order and payment status.
- Actions to view order details or continue shopping.
15.2 Example Confirmation Component
import React from "react";
import { View, Text, Button } from "react-native";
export default function OrderConfirmationScreen({
route,
navigation
}) {
const { order } = route.params;
return (
<View>
<Text>Thank you for your order!</Text>
<Text>Order ID: {order.id}</Text>
<Text>Status: {order.status}</Text>
<Text>
Total: ₹{Number(order.total).toFixed(2)}
</Text>
<Button
title="View Orders"
onPress={() => navigation.navigate("OrderHistory")}
/>
<Button
title="Continue Shopping"
onPress={() => navigation.navigate("Products")}
/>
</View>
);
}
The component assumes that the backend returns an order object containing the displayed fields. Adjust it to match the actual API response, and format the currency according to the order's currency code.
16. Displaying Order History
Order history allows authenticated customers to review their past purchases. It should retrieve only orders the current user is authorized to view.
16.1 Example Order History Data
const order = {
id: "ord_1001",
createdAt: "2026-10-21T08:30:00Z",
status: "Processing",
total: 4980,
currency: "INR",
itemCount: 2
};
16.2 Fetch Order History
export async function getOrders(accessToken) {
const response = await fetch(
"https://api.example.com/orders",
{
headers: {
Authorization: `Bearer ${accessToken}`
}
}
);
if (!response.ok) {
throw new Error("Unable to retrieve order history.");
}
return response.json();
}
Use pagination for customers with many orders. Provide loading, empty, and error states, and refresh order status from the backend rather than assuming the status remains unchanged after checkout.
17. Integrating Payment Processing
Some e-commerce applications support cash on delivery, while others require online payments. Online payments should be integrated through a reputable payment provider and a backend-controlled workflow.
17.1 Typical Online Payment Flow
- The application requests order creation or payment initiation from the backend.
- The backend calculates the amount and creates the appropriate payment session or intent with the payment provider.
- The application opens the provider's approved checkout interface or SDK.
- The customer completes or cancels the payment process.
- The backend verifies payment status using trusted provider information, such as a verified webhook.
- The application retrieves the final order and payment status from the backend.
17.2 Payment Security Rules
- Never store raw card numbers or card security codes in the application's own storage.
- Never trust a client-only payment success message as proof of payment.
- Keep secret API keys on the server, not in the React Native bundle.
- Verify payment notifications on the backend.
- Use idempotency where supported to avoid duplicate charges or orders.
- Use the payment provider's official SDK or hosted checkout flow.
18. Authentication and User Accounts
Account functionality lets users manage personal details, addresses, and order history. Authentication is commonly handled through a backend service or a trusted identity provider.
18.1 Common Authentication Features
- Customer registration.
- Login and logout.
- Session or token renewal.
- Protected order-history requests.
- Address management.
- Unauthorized-session handling.
18.2 Secure Token Handling
Do not place passwords, long-lived secrets, or sensitive authentication data in plain-text application storage. Use an appropriate secure storage mechanism for credentials or tokens where required. The backend must validate the identity and authorization of each protected request.
19. Navigation Between E-Commerce Screens
Navigation provides a consistent route between product browsing, product details, the cart, checkout, order confirmation, and order history.
19.1 Suggested Screen Routes
| Screen |
Purpose |
| Products |
Browse and search products. |
| ProductDetails |
View a selected product. |
| Cart |
Review and update cart items. |
| Checkout |
Enter delivery and payment details. |
| OrderConfirmation |
Display the created order information. |
| OrderHistory |
Review previous orders. |
| OrderDetails |
View a specific order's information. |
19.2 Navigation Best Practices
- Use stable route names.
- Pass identifiers instead of large, stale objects when appropriate.
- Prevent navigation to checkout when the cart is empty.
- Preserve checkout state when users temporarily navigate back.
- Handle deep links carefully if order links or notifications open specific screens.
- Ensure protected screens check authentication and authorization.
20. Search, Filtering, and Sorting Products
Product discovery becomes more useful as the catalogue grows. Search, filtering, and sorting allow users to find relevant items efficiently.
20.1 Common Search and Filter Features
- Search by product name or keyword.
- Filter by category.
- Filter by price range.
- Filter by brand, size, or color.
- Sort by price, popularity, or newest products.
- Display a message when no products match the search.
20.2 Example Client-Side Filtering
const filteredProducts = products.filter((product) => {
const matchesSearch = product.name
.toLowerCase()
.includes(searchText.toLowerCase());
const matchesCategory =
selectedCategory === "All" ||
product.category === selectedCategory;
return matchesSearch && matchesCategory;
});
Client-side filtering is suitable for a small dataset. For large catalogues, use backend search, filtering, sorting, and pagination to reduce network transfer and device processing.
21. Error Handling and User Feedback
A robust shopping experience should handle expected failures without losing user progress unnecessarily.
21.1 Common Error Scenarios
- Product data cannot be loaded.
- A product becomes unavailable.
- The selected quantity exceeds available stock.
- The network connection is interrupted.
- The checkout form contains invalid information.
- The order request fails or times out.
- The authentication session expires.
- The payment provider reports a failed or pending payment.
- The cart cannot be saved locally.
21.2 Error Handling Best Practices
- Show messages that explain what the user can do next.
- Keep cart contents when an order attempt fails.
- Allow retry for recoverable network errors.
- Prevent duplicate requests while a submission is running.
- Do not expose internal server details or sensitive data to users.
- Log technical details securely for debugging.
- Re-fetch current prices and stock before confirming an order.
22. Security Considerations
E-commerce applications process customer information and may handle payments. Security must be considered throughout the development process.
- HTTPS: Use encrypted connections for all production API communication.
- Server-side validation: Validate product identifiers, quantities, prices, discounts, addresses, and permissions on the server.
- Authorization: Ensure users can only view or modify their own protected resources.
- Secret management: Do not bundle private API secrets into a mobile application.
- Payment verification: Verify payment outcomes on the backend.
- Input handling: Validate and normalize user input.
- Data minimization: Collect only the customer data required for the service.
- Safe logging: Avoid logging passwords, payment credentials, access tokens, or unnecessary personal information.
- Dependency updates: Keep libraries and native dependencies maintained.
- Privacy: Follow applicable privacy and data-retention requirements.
23. Performance Optimization
Product lists can contain many images and records. Performance optimization helps the application remain responsive on a wide range of devices.
23.1 Performance Techniques
- Use FlatList for long lists.
- Provide stable keys for list items.
- Use appropriately sized and compressed product images.
- Implement pagination or infinite scrolling when necessary.
- Avoid unnecessary state updates and re-renders.
- Memoize expensive computations when profiling shows a benefit.
- Debounce search input when sending frequent requests to the backend.
- Cache product data where appropriate and define a refresh strategy.
- Keep large API operations off the main user-interaction path.
- Display loading placeholders to improve perceived responsiveness.
23.2 Avoid Unnecessary Re-Renders
Split large screens into reusable components and keep state close to the components that need it. Use React.memo, useMemo, or useCallback when they address a measured performance issue rather than adding them everywhere.
24. Testing the E-Commerce Application
Testing helps confirm that product browsing, cart updates, checkout, and order creation behave correctly in normal and failure scenarios.
24.1 Unit Testing
Test individual functions such as subtotal calculations, total calculations, quantity updates, cart removal, and checkout validation.
test("calculates cart subtotal", () => {
const items = [
{ productId: "p1", price: 100, quantity: 2 },
{ productId: "p2", price: 50, quantity: 1 }
];
expect(calculateSubtotal(items)).toBe(250);
});
24.2 Integration Testing
- Verify product screens display API results.
- Verify the cart badge updates after adding a product.
- Verify cart changes are reflected in the cart screen.
- Verify checkout validation blocks invalid submissions.
- Verify a successful order displays the expected confirmation.
- Verify failed order requests preserve the cart appropriately.
24.3 Manual Testing Checklist
- Open the product listing and check that products render correctly.
- Open product details from the listing.
- Add a product to the cart.
- Add the same product again and verify quantity handling.
- Increase and decrease the quantity.
- Remove an item and verify the subtotal changes.
- Close and reopen the app to test cart persistence.
- Try to check out with an empty cart.
- Submit incomplete checkout details.
- Simulate a network failure during order creation.
- Verify order history and status updates.
- Test on representative Android and iOS devices.
25. Common Development Problems and Solutions
| Problem |
Possible Cause |
Solution |
| Products do not load |
Incorrect API URL, server failure, or network issue. |
Check the endpoint, response status, connectivity, and logs. |
| Product images are missing |
Invalid image URL or inaccessible resource. |
Validate image URLs and provide a fallback image. |
| Cart count is incorrect |
Count is based on the number of distinct products rather than quantities. |
Sum each cart item's quantity. |
| Subtotal does not update |
State was mutated or calculations use stale values. |
Use immutable updates and derive totals from current state. |
| Cart disappears after restart |
Only in-memory state is used. |
Implement local persistence or a server-managed cart. |
| Duplicate orders appear |
Repeated submissions or retry requests. |
Disable duplicate submissions and implement backend idempotency. |
| Order total is incorrect |
Client total is trusted or pricing rules are inconsistent. |
Recalculate the authoritative amount on the backend. |
| Checkout fails unexpectedly |
Invalid input, unavailable stock, or server validation errors. |
Display actionable errors and refresh current product availability. |
| Order history is exposed incorrectly |
Missing backend authorization checks. |
Authorize each request against the authenticated user. |
26. Best Practices for a Production-Ready E-Commerce App
- Use reusable components for products, cart items, buttons, and order summaries.
- Keep network requests in service modules rather than scattering them across UI components.
- Use a consistent error-handling strategy.
- Separate cart state from order state.
- Persist the cart carefully and validate stored data before using it.
- Use the backend as the source of truth for stock, pricing, order status, and payment status.
- Make order creation resilient to retries and duplicate requests.
- Use secure authentication and authorization for protected operations.
- Provide accessible labels and sufficiently clear touch targets.
- Test offline, slow-network, and server-error scenarios.
- Monitor performance and crashes after release.
- Document the API contract and keep frontend and backend models aligned.
27. Practical Project: Build a Mini E-Commerce App
The goal of this project is to build a mobile application where customers can browse products, manage a shopping cart, complete checkout, and view orders.
27.1 Project Requirements
- Create a product listing screen with images, names, and prices.
- Build a product details screen.
- Add an Add to Cart button.
- Build a cart screen with quantity controls and removal actions.
- Calculate the cart subtotal and display a summary.
- Persist cart contents across application restarts.
- Create a checkout form with validation.
- Connect the app to a backend order endpoint.
- Display an order confirmation after successful order creation.
- Build an order history screen.
- Add loading, empty, and error states.
- Test normal flows and failure scenarios.
27.2 Suggested Development Phases
| Phase |
Tasks |
Expected Result |
| Phase 1 |
Set up project and navigation. |
Application screens are connected. |
| Phase 2 |
Build product listing and details. |
Users can browse products. |
| Phase 3 |
Create cart context and cart screen. |
Users can manage selected items. |
| Phase 4 |
Add pricing and local persistence. |
Cart totals are derived and data can persist. |
| Phase 5 |
Build checkout validation. |
Invalid forms are blocked. |
| Phase 6 |
Integrate backend order creation. |
Orders are submitted to the server. |
| Phase 7 |
Add confirmation and order history. |
Users can review their purchases. |
| Phase 8 |
Test, optimize, and refine the interface. |
The app is more reliable and user-friendly. |
28. Interview Questions and Answers
Q1. What is an e-commerce application?
An e-commerce application enables customers to browse products, add items to a cart, complete checkout, place orders, and review their purchases.
Q2. Why is React Native suitable for e-commerce development?
React Native enables developers to build mobile interfaces for Android and iOS using React concepts and a shared JavaScript or TypeScript codebase, with access to native platform capabilities where needed.
Q3. What is the purpose of a shopping cart?
A shopping cart maintains the products a customer intends to purchase, including selected quantities, and supports operations such as adding, removing, and updating items.
Q4. Why use React Context for cart management?
React Context makes shared cart data and operations available to multiple components without passing props through every intermediate component.
Q5. How do you calculate a cart subtotal?
Multiply each item's unit price by its quantity and add all line totals together.
Q6. What is the difference between a cart and an order?
A cart represents items the customer is considering purchasing. An order is a backend-managed purchase record created through the checkout process.
Q7. Why should the backend calculate the final order total?
Client-side values can be changed or become outdated. The backend must validate current prices, discounts, stock, shipping, and taxes before creating the authoritative order.
Q8. How can cart data persist across app restarts?
Cart data can be stored locally using a suitable storage mechanism or maintained by the backend for authenticated customers. The correct choice depends on synchronization and account requirements.
Q9. What is a REST API?
A REST API exposes resources through HTTP endpoints and methods. A mobile application can use these endpoints to retrieve products, create orders, and access order history.
Q10. Why should order submission prevent duplicate requests?
Repeated requests can accidentally create duplicate orders. Client-side submission controls and backend idempotency help prevent this problem.
Q11. How should online payments be integrated?
Use a trusted payment provider's supported SDK or hosted checkout. The backend should create the payment session and verify the final payment status using trusted provider information.
Q12. What is FlatList used for?
FlatList efficiently renders long lists of data, such as product catalogues and order history, by rendering list items as needed.
Q13. How should API errors be handled?
Catch request failures, check HTTP response status, show a useful message, provide retry options where appropriate, and preserve user data when possible.
Q14. What is the difference between client-side and server-side validation?
Client-side validation gives immediate feedback to the user. Server-side validation enforces the actual business and security rules and must be performed even when client validation exists.
Q15. What are important e-commerce security practices?
Use HTTPS, secure authentication, backend authorization, server-side pricing and stock validation, secure token handling, verified payment status, and careful logging of personal information.
29. Practice Exercises
- Create a product card with an image, product name, and price.
- Fetch products from a REST API and display them with FlatList.
- Implement product detail navigation using a product ID.
- Add products to a cart using React Context.
- Implement quantity increase, decrease, and item removal.
- Display the total number of units in the cart badge.
- Write a reusable function to calculate cart subtotal.
- Persist the cart using AsyncStorage.
- Create a checkout form with required-field validation.
- Implement an order service that handles HTTP errors.
- Display an order confirmation from the backend response.
- Fetch and display authenticated order history.
- Test what happens when the network fails during checkout.
- Prevent duplicate order submissions and reconcile uncertain outcomes with the backend.
- Add search, category filtering, and product sorting.
30. Quick Revision Checklist
- Understand the product, cart, checkout, and order lifecycle.
- Know how to create reusable React Native components.
- Understand FlatList and navigation.
- Know how to fetch product data from a REST API.
- Understand immutable state updates.
- Know how to share cart state with React Context.
- Calculate subtotal and display pricing information correctly.
- Understand local cart persistence and its limitations.
- Validate checkout forms on the client and server.
- Submit orders through a backend API.
- Handle loading, success, empty, and error states.
- Understand order statuses and payment verification.
- Apply secure authentication and authorization.
- Test cart operations and order submission thoroughly.
31. Conclusion
Building an e-commerce application with products, cart, and orders is a comprehensive React Native project that combines UI development, API integration, state management, navigation, data persistence, form validation, and backend communication.
A successful implementation should provide a smooth product browsing experience, accurate cart updates, clear checkout information, reliable order creation, and useful order history. Most importantly, the backend must remain the source of truth for pricing, inventory, authorization, order status, and payment verification.
To learn more about mobile application development, visit React Native Training at JustAcademy or register for a course demo. You can also explore the JustAcademy website.