export interface CartItem {
sku: string;
name: string;
unitPrice: number; // cents
qty: number;
}
export interface CartTotals {
subtotal: number;
discountTotal: number;
total: number;
}
export type DiscountRule = (items: CartItem[], coupons: Set<string>) => number | null;
const lineTotal = (i: CartItem): number => i.unitPrice * i.qty;
const subtotalOf = (items: CartItem[]): number => items.reduce((s, i) => s + lineTotal(i), 0);
export function percentOff(coupon: string, pct: number): DiscountRule {
return (items, coupons) => {
if (!coupons.has(coupon)) return null;
return Math.round(subtotalOf(items) * (pct / 100));
};
}
export function bogo(sku: string): DiscountRule {
return (items) => {
const line = items.find((i) => i.sku === sku);
if (!line || line.qty < 2) return null;
return Math.floor(line.qty / 2) * line.unitPrice;
};
}
export function priceCart(
items: CartItem[],
coupons: Set<string>,
rules: DiscountRule[]
): CartTotals {
const subtotal = subtotalOf(items);
const discountTotal = rules.reduce((sum, rule) => sum + (rule(items, coupons) ?? 0), 0);
const total = Math.max(0, subtotal - discountTotal);
return { subtotal, discountTotal: Math.min(discountTotal, subtotal), total };
}
import { CartItem } from './pricing';
export interface CartState {
items: CartItem[];
appliedCoupons: string[];
}
export type CartAction =
| { type: 'ADD_ITEM'; item: CartItem }
| { type: 'SET_QTY'; sku: string; qty: number }
| { type: 'REMOVE_ITEM'; sku: string }
| { type: 'APPLY_COUPON'; code: string }
| { type: 'CLEAR' };
export const initialCart: CartState = { items: [], appliedCoupons: [] };
export function cartReducer(state: CartState, action: CartAction): CartState {
switch (action.type) {
case 'ADD_ITEM': {
const existing = state.items.find((i) => i.sku === action.item.sku);
const items = existing
? state.items.map((i) =>
i.sku === action.item.sku ? { ...i, qty: i.qty + action.item.qty } : i
)
: [...state.items, action.item];
return { ...state, items };
}
case 'SET_QTY': {
if (action.qty <= 0) {
return { ...state, items: state.items.filter((i) => i.sku !== action.sku) };
}
return {
...state,
items: state.items.map((i) =>
i.sku === action.sku ? { ...i, qty: action.qty } : i
),
};
}
case 'REMOVE_ITEM':
return { ...state, items: state.items.filter((i) => i.sku !== action.sku) };
case 'APPLY_COUPON':
if (state.appliedCoupons.includes(action.code)) return state;
return { ...state, appliedCoupons: [...state.appliedCoupons, action.code] };
case 'CLEAR':
return initialCart;
default:
return state;
}
}
import { useCallback, useMemo, useReducer } from 'react';
import { cartReducer, initialCart } from './cartReducer';
import { CartItem, DiscountRule, bogo, percentOff, priceCart } from './pricing';
const rules: DiscountRule[] = [
percentOff('SAVE10', 10),
bogo('SKU-COFFEE'),
];
export function useCart() {
const [state, dispatch] = useReducer(cartReducer, initialCart);
const totals = useMemo(
() => priceCart(state.items, new Set(state.appliedCoupons), rules),
[state]
);
const addItem = useCallback((item: CartItem) => dispatch({ type: 'ADD_ITEM', item }), []);
const setQty = useCallback(
(sku: string, qty: number) => dispatch({ type: 'SET_QTY', sku, qty }),
[]
);
const removeItem = useCallback((sku: string) => dispatch({ type: 'REMOVE_ITEM', sku }), []);
const applyCoupon = useCallback(
(code: string) => dispatch({ type: 'APPLY_COUPON', code }),
[]
);
const clear = useCallback(() => dispatch({ type: 'CLEAR' }), []);
return {
items: state.items,
coupons: state.appliedCoupons,
totals,
addItem,
setQty,
removeItem,
applyCoupon,
clear,
};
}
This snippet models a shopping cart where line items live in a useReducer state machine and all money math is delegated to a pure pricing service. The separation is deliberate: the reducer owns what is in the cart, while pricing.ts owns what the cart costs. Keeping the two apart means the discount logic can be unit-tested in isolation and re-run deterministically without a running component tree.
In pricing.ts, prices are represented in integer cents to avoid floating-point drift, a classic e-commerce pitfall where 0.1 + 0.2 fails to equal 0.3. Discount rules are modeled as an array of DiscountRule objects, each a pure function returning either a discount or null. percentOff and bogo are rule factories, so new promotions are composed by pushing more rules into the list rather than editing a growing conditional. priceCart folds every rule over the current items, sums the applicable discounts, and returns a CartTotals breakdown of subtotal, discountTotal, and total, clamped so a stack of promotions can never drive the total negative.
In cartReducer.ts, the state is just the raw CartItem[] plus an appliedCoupons set — intentionally free of any computed money. ADD_ITEM merges quantities for an existing SKU instead of duplicating a row, SET_QTY removes the line when quantity hits zero, and every case returns a new array so React sees a fresh reference. Modeling the cart as an event-driven reducer makes each mutation an auditable, replayable action, which is valuable when carts must survive refreshes or sync across tabs.
In useCart.ts, the hook wires the reducer to the pricing service. The expensive priceCart fold is wrapped in useMemo keyed on state, so totals recompute only when items or coupons actually change, not on every render. The hook exposes ergonomic action creators (addItem, setQty, applyCoupon) alongside the derived totals, giving components a clean API while the reducer and pricing internals stay hidden.
The trade-off is an extra fold on each cart change, but for realistic cart sizes that cost is negligible and buys full determinism. This layering — reducer for state, pure service for derivation, memoized hook to bridge them — scales cleanly as promotions, taxes, and shipping rules accumulate.
Related snips
class Money
include Comparable
attr_reader :amount, :currency
def initialize(amount, currency = 'USD')
Value objects for domain modeling
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
timeout: 15000,
Axios API client with interceptors
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface FilterState {
search: string
category: string | null
Zustand for lightweight state management
import SwiftUI
struct ContentView: View {
@State private var username = ""
@State private var isLoggedIn = false
@StateObject private var viewModel = LoginViewModel()
SwiftUI declarative UI with state management
import React from "react";
type FallbackProps = {
error: Error;
reset: () => void;
};
React Error Boundary + error reporting hook
Share this code
Here's the card — post it anywhere.