45 minlesson

Phase 4: Results Display & Performance Optimization

Phase 4: Results Display & Performance Optimization

Overview

Transform raw rate data into an insightful, performant user interface. You'll build comparison tables, implement intelligent sorting and filtering, and apply React 19's performance features to ensure a smooth user experience even with multiple carriers and complex calculations.

Learning Objectives

By the end of this phase, you will:

  • Create data-rich UI components with React 19 Suspense
  • Implement performance optimizations using useMemo and useCallback
  • Build interactive filtering and sorting features
  • Design visual comparison aids and recommendations
  • Handle loading and error states gracefully
  • Persist and restore calculation results

Requirements

1. Results Display Components

Main Results Page Structure:

Create src/app/results/page.tsx as a client component that:

  • Imports and uses React 19's Suspense component
  • Uses Next.js useSearchParams hook to access URL params
  • Renders a container with:
    • Page title "Shipping Rate Comparison"
    • Suspense boundary wrapping RatesDisplay component
    • ResultsSkeletonLoader as the fallback during loading

Required Components:

Create the following components in src/components/results/:

Main Components:

  • RatesDisplay - Container that fetches rate data (using Suspense) and conditionally renders error messages, empty state, or results table
  • RatesComparisonTable - Desktop-optimized table view for comparing rates
  • RateCard - Mobile-friendly card view for individual rates

Supporting Components:

  • RatesFilters - Controls for filtering carriers and sorting results
  • BestValueBadge - Visual indicator for cheapest/fastest/best-value options
  • ResultsSkeletonLoader - Animated loading placeholder
  • RatesErrorDisplay - Shows partial failures from carriers
  • NoRatesFound - Empty state when no rates are available

2. Suspense Integration with use() Hook

Goal: Implement React 19's Suspense for data fetching with automatic loading states.

Review: React 19 Mastery course, Topics 8-9 - Suspense and use() hook

File to Create: src/lib/rates-api.ts

fetchRates Function:

Create async fetchRates(request: RateRequest): Promise<RateResponse> function that:

  • Makes POST request to /api/rates endpoint
  • Sends RateRequest as JSON body
  • Returns parsed RateResponse
  • Throws error if response not ok

RatesDisplay Component:

Create src/components/results/RatesDisplay.tsx as a client component that:

  • Receives a ratesPromise prop (Promise)
  • Uses React 19's use() hook to consume the promise directly
  • Conditionally renders:
    • Error display if response has errors
    • Empty state if no rates found
    • Results summary and comparison table if rates exist

Results Page with Suspense:

In src/app/results/page.tsx:

  • Create the promise at the page level (outside the Suspense boundary)
  • Pass the promise to RatesDisplay inside the Suspense boundary
  • Suspense automatically shows fallback while promise is pending

How React 19 Suspense Works:

  1. Parent component creates the promise and passes it down
  2. Child component calls use(promise) to read the value
  3. If promise is pending, React suspends and shows the fallback
  4. When promise resolves, React re-renders with the data
  5. If promise rejects, the nearest Error Boundary catches it

3. Comparison Table Implementation

Goal: Build an interactive, sortable table for desktop users to compare shipping rates.

File to Create: src/components/results/RatesComparisonTable.tsx

Component Props:

  • rates: ShippingRate[] - Array of shipping rates to display

State Management:

Define types and use useState to track:

  • sortField: 'price' | 'speed' | 'carrier' - Current sort column
  • sortDirection: 'asc' | 'desc' - Current sort order
  • selectedCarriers: CarrierName[] - Array of carriers to filter by

Memoized Calculations:

Use useMemo for performance:

  1. displayedRates - Filtered and sorted rates:

    • Filter by selected carriers if any are selected
    • Create a copy before sorting (use spread operator or toSorted()) to avoid mutating props
    • Sort by current field and direction
    • Dependencies: rates, sortField, sortDirection, selectedCarriers
  2. bestValues - Best value recommendations:

    • Handle empty rates array (return null values if empty)
    • Find cheapest rate (lowest totalCost)
    • Find fastest rate (earliest estimatedDeliveryDate)
    • Calculate best value using scoring: totalCost + (businessDays × 2)
    • Dependencies: rates

Event Handlers:

Use useCallback for handlers passed to child components:

  • handleSort(field) - Toggles direction if same field, otherwise sets new field with 'asc'

Table Structure:

Render HTML table with:

  • Header row with sortable columns (carrier, service, price, delivery, features)
  • Clickable headers that call handleSort()
  • Visual sort indicators (up/down arrows) showing current sort field/direction
  • Body rows mapping over displayedRates:
    • Carrier logo and name
    • Service details (name and code)
    • Total cost with expandable fee breakdown
    • Best value badge for cheapest rate
    • Estimated delivery date and business days count
    • Best value badge for fastest rate
    • Features list (chips or comma-separated)

Helper Components to Create:

  • SortIcon - Displays up/down arrow based on direction
  • CarrierLogo - Displays carrier logo image
  • FeeBreakdown - Expandable list showing base rate + fees
  • FeaturesList - Displays rate features

Styling: Use Tailwind CSS with hover states, proper spacing, and responsive overflow handling.

4. Mobile-Responsive Card View

Goal: Create mobile-friendly card layout for rate comparison.

File to Create: src/components/results/RateCard.tsx

Component Requirements:

Create RateCard component that:

  • Accepts rate (ShippingRate) and isBestValue (boolean) props
  • Uses useState to track expanded state for fee breakdown

Card Structure:

Design a card with:

  1. Header section:

    • Carrier logo (large size for mobile)
    • Carrier name and service name
    • Best value badge if applicable
  2. Two-column grid displaying:

    • Left: Total cost (large, bold, green text)
    • Right: Delivery date and business days count
  3. Expandable fee breakdown:

    • Toggle button showing "Show/Hide fee breakdown"
    • When expanded, display:
      • Base rate
      • List of all additional fees with amounts
    • Use collapsible div with border-top
  4. Features list (compact format)

  5. Action button - Full-width "Select This Rate" button

Styling:

  • Rounded card with shadow
  • Border that highlights on hover (transparent → blue)
  • Proper spacing and padding for touch targets
  • Responsive grid layout
  • Smooth transitions for interactions

5. Filters and Sorting Controls

File to Create: src/components/results/RatesFilters.tsx

Component Requirements:

Create RatesFilters component that:

  • Accepts onFilterChange and onSortChange callback props
  • Renders in a responsive grid (3 columns on desktop, stacked on mobile)

Filter Controls:

  1. Carrier Filter:

    • Checkbox group for selecting carriers
    • Component: CarrierCheckboxGroup
    • Allows multiple carrier selection
  2. Sort By Dropdown:

    • Select element with options:
      • "Lowest Price" (value: 'price')
      • "Fastest Delivery" (value: 'speed')
      • "Carrier Name" (value: 'carrier')
    • Triggers onSortChange with selected value
  3. Delivery Speed Filter:

    • Component: ServiceSpeedFilter
    • Filters by service speed tiers

Styling:

  • Light gray background container
  • Rounded corners with padding
  • Proper label styling
  • Responsive grid layout

6. Performance Optimization

Goal: Optimize React re-renders and expensive calculations.

Review: React 19 Mastery course, Topic 10 - Performance Optimization

Optimization Techniques to Implement:

  1. useMemo for Expensive Calculations:

    • Memoize sorted/filtered rates - recalculate only when rates, sortField, sortDirection, or filters change
    • Memoize best value recommendations - recalculate only when rates array changes
    • Memoize value score calculations
  2. useCallback for Event Handlers:

    • Memoize handleCarrierToggle callback to prevent child re-renders
    • Memoize handleSortChange callback
    • Memoize all event handlers passed as props to child components
  3. React.memo for Components:

    • Wrap RateCard in React.memo with custom comparison function
    • Only re-render if rate.id or isBestValue props change
    • Prevents unnecessary re-renders when parent updates
  4. Avoid Inline Object/Array Creation:

    • Don't create new objects or arrays in render
    • Extract to constants or useMemo when needed

Measuring Performance:

  • Use React DevTools Profiler to identify slow components
  • Monitor re-render counts
  • Optimize components that render frequently

7. Results Persistence

Goal: Cache rate results to improve UX and reduce API calls.

File to Create: src/lib/results-storage.ts

Implementation Requirements:

  1. Define Constants:

    • Storage key: 'shipping-rate-results'
    • TTL (time to live): 30 minutes (1000 × 60 × 30 milliseconds)
  2. Create StoredResults Interface:

    • response: RateResponse
    • request: RateRequest
    • timestamp: number (milliseconds)
  3. Implement Functions:

saveResults(request, response):

  • Create StoredResults object with current timestamp
  • Serialize to JSON
  • Save to localStorage with defined key

loadResults():

  • Retrieve from localStorage
  • Return null if not found
  • Parse JSON to StoredResults
  • Check if expired (current time - timestamp > TTL)
  • If expired, call clearResults() and return null
  • Otherwise, return the stored data

clearResults():

  • Remove item from localStorage

Usage: Call saveResults() after successful rate fetch, call loadResults() on page load to check for cached results.


Deliverables

By the end of Phase 4, you must have:

  • Responsive results display (desktop table + mobile cards)
  • React 19 Suspense integration with use() hook
  • Sorting and filtering functionality
  • Best value recommendations with visual badges
  • Fee breakdown display (collapsible)
  • Performance optimizations (useMemo, useCallback, memo)
  • Results persistence to localStorage
  • Loading skeletons and error states
  • Accessibility-compliant UI (keyboard navigation, ARIA labels)
  • Works flawlessly on mobile (320px+) and desktop

Validation Checklist

  • Results display correctly on all screen sizes
  • Suspense shows loading state during fetch
  • Sorting and filtering update instantly
  • Best value badges appear on correct rates
  • Fee breakdown shows all additional costs
  • No unnecessary re-renders (check with React DevTools Profiler)
  • Results persist across page refreshes
  • Keyboard navigation works for all interactive elements
  • Screen reader announces important changes

Testing Requirements

Component Tests:

Create test file: src/components/results/__tests__/RatesComparisonTable.test.tsx

Write tests to verify:

  • Sorting by price works correctly (ascending and descending)
  • Sorting by delivery date works correctly
  • Sorting by carrier name works alphabetically
  • Carrier filtering removes unwanted carriers from display
  • Best value identification correctly finds cheapest and fastest rates
  • Value score calculation balances price and speed appropriately

Performance Tests:

Create test file: src/components/results/__tests__/Performance.test.tsx

Write tests to verify:

  • Components memoized with React.memo don't re-render when props unchanged
  • useMemo dependencies work correctly (recalculates only when needed)
  • useCallback prevents function recreation on every render
  • Profiler measurements show acceptable render times

Resources


Estimated Time

  • Comparison table: 3 hours
  • Mobile cards: 2 hours
  • Filters and sorting: 2 hours
  • Suspense integration: 2 hours
  • Performance optimization: 2 hours
  • Testing: 2 hours
  • Total: 13 hours

Next Steps

Proceed to Phase 5: Testing, Optimization & Deployment for comprehensive testing, final optimizations, and deployment preparation.