Skip to content

React Integration Guide

Learn how to integrate route optimization and mapping into your React applications using @route-optimization/react.

Installation

bash
npm install @route-optimization/react @route-optimization/core
# or
pnpm add @route-optimization/react @route-optimization/core
# or
yarn add @route-optimization/react @route-optimization/core

Core Concepts

The React package provides hooks that wrap the core functionality with React-specific state management:

  • useRouteMap - Main hook for map rendering and route management
  • useMapControls - Hook for map interaction controls
  • useRouteOptimization - Hook for route calculation and optimization
  • useOptimizationStatus - Hook for optimization progress tracking

Basic Map Rendering

Simple Route Map

tsx
import { useRouteMap } from '@route-optimization/react';

function RouteMapComponent() {
  const { mapRef, renderRoute, clearRoute, isReady } = useRouteMap({
    apiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
    center: { lat: 13.7563, lng: 100.5018 }, // Bangkok
    zoom: 12,
  });

  const handleShowRoute = () => {
    renderRoute({
      stops: [
        {
          id: '1',
          location: { lat: 13.7563, lng: 100.5018 },
          name: 'Start Location',
        },
        {
          id: '2',
          location: { lat: 13.7467, lng: 100.5352 },
          name: 'End Location',
        },
      ],
    });
  };

  return (
    <div>
      <div ref={mapRef} style={{ width: '100%', height: '600px' }} />
      <button onClick={handleShowRoute} disabled={!isReady}>
        Show Route
      </button>
      <button onClick={clearRoute} disabled={!isReady}>
        Clear Route
      </button>
    </div>
  );
}

Route Map View Component

For simpler usage, use the pre-built RouteMapView component:

tsx
import { RouteMapView } from '@route-optimization/react';

function App() {
  const [route, setRoute] = useState(null);

  return (
    <RouteMapView
      apiKey="YOUR_GOOGLE_MAPS_API_KEY"
      route={route}
      mapConfig={{
        center: { lat: 13.7563, lng: 100.5018 },
        zoom: 12,
      }}
      onError={(error) => console.error('Map error:', error)}
    />
  );
}

Map Controls

Using Map Controls Hook

tsx
import { useRouteMap, useMapControls } from '@route-optimization/react';

function InteractiveMap() {
  const { mapRef, map, isReady } = useRouteMap({
    apiKey: 'YOUR_API_KEY',
  });

  const { center, zoom, bounds, setCenter, setZoom, fitBounds } = useMapControls(map);

  const handleCenterBangkok = () => {
    setCenter({ lat: 13.7563, lng: 100.5018 });
  };

  const handleZoomIn = () => {
    setZoom((zoom ?? 12) + 1);
  };

  return (
    <div>
      <div ref={mapRef} style={{ width: '100%', height: '600px' }} />

      <div className="controls">
        <p>
          Center: {center?.lat.toFixed(4)}, {center?.lng.toFixed(4)}
        </p>
        <p>Zoom: {zoom}</p>

        <button onClick={handleCenterBangkok}>Center on Bangkok</button>
        <button onClick={handleZoomIn}>Zoom In</button>
      </div>
    </div>
  );
}

Route Optimization

Basic Optimization

tsx
import { useRouteOptimization } from '@route-optimization/react';

function RouteOptimizer() {
  const { status, data, error, isLoading, isSuccess, optimize, reset } = useRouteOptimization({
    useMockMode: true, // Use mock mode for development
  });

  const handleOptimize = async () => {
    const request = {
      shipments: [
        {
          id: 'shipment-1',
          deliveryLocation: { latitude: 13.7563, longitude: 100.5018 },
        },
        {
          id: 'shipment-2',
          deliveryLocation: { latitude: 13.7467, longitude: 100.5352 },
        },
      ],
      vehicles: [
        {
          id: 'vehicle-1',
          startLocation: { latitude: 13.7563, longitude: 100.5018 },
        },
      ],
    };

    try {
      const response = await optimize(request);
      console.log('Optimization result:', response);
    } catch (err) {
      console.error('Optimization failed:', err);
    }
  };

  return (
    <div>
      <button onClick={handleOptimize} disabled={isLoading}>
        {isLoading ? 'Optimizing...' : 'Optimize Routes'}
      </button>

      {isSuccess && data && (
        <div>
          <h3>Optimization Complete!</h3>
          <p>Routes: {data.routes?.length ?? 0}</p>
          <p>Total Distance: {data.metrics?.totalDistance ?? 0} km</p>
        </div>
      )}

      {error && <div className="error">Error: {error}</div>}
    </div>
  );
}

Production Mode with Google Cloud

tsx
import { useRouteOptimization } from '@route-optimization/react';

function ProductionOptimizer() {
  const { optimize } = useRouteOptimization({
    useMockMode: false,
    projectId: 'your-google-cloud-project',
    credentials: {
      client_email: 'service-account@project.iam.gserviceaccount.com',
      private_key: process.env.GOOGLE_PRIVATE_KEY,
    },
  });

  // ... rest of component
}

Progress Tracking

tsx
import { useRouteOptimization, useOptimizationStatus } from '@route-optimization/react';

function OptimizationWithProgress() {
  const { optimize } = useRouteOptimization({ useMockMode: true });

  const {
    progress,
    step,
    setProgress,
    reset: resetProgress,
  } = useOptimizationStatus({
    onStart: () => console.log('Optimization started'),
    onSuccess: () => console.log('Optimization complete!'),
    onError: (error) => console.error('Failed:', error),
  });

  const handleOptimize = async () => {
    resetProgress();
    setProgress(0, 'Initializing...');

    // Simulate progress
    setProgress(25, 'Calculating distances...');

    const response = await optimize(request);

    setProgress(75, 'Optimizing routes...');

    if (response.success) {
      setProgress(100, 'Complete!');
    }
  };

  return (
    <div>
      <button onClick={handleOptimize}>Optimize</button>

      <div className="progress">
        <div className="progress-bar" style={{ width: `${progress}%` }} />
        <p>{step}</p>
      </div>
    </div>
  );
}

Complete Example

Combining map rendering with route optimization:

tsx
import { useState } from 'react';
import { useRouteMap, useRouteOptimization, RouteMapView } from '@route-optimization/react';
import type { Route } from '@route-optimization/core';

function CompleteApp() {
  const [route, setRoute] = useState<Route | null>(null);
  const [locations, setLocations] = useState([
    { lat: 13.7563, lng: 100.5018, name: 'Bangkok' },
    { lat: 13.7467, lng: 100.5352, name: 'Chatuchak' },
    { lat: 13.7307, lng: 100.5418, name: 'Victory Monument' },
  ]);

  const { optimize, isLoading, isSuccess } = useRouteOptimization({
    useMockMode: true,
  });

  const handleOptimize = async () => {
    const request = {
      shipments: locations.map((loc, i) => ({
        id: `shipment-${i}`,
        deliveryLocation: { latitude: loc.lat, longitude: loc.lng },
      })),
      vehicles: [
        {
          id: 'vehicle-1',
          startLocation: { latitude: locations[0].lat, longitude: locations[0].lng },
        },
      ],
    };

    const response = await optimize(request);

    if (response.success && response.routes?.[0]) {
      // Convert optimization result to Route format
      const optimizedRoute: Route = {
        stops:
          response.routes[0].stops?.map((stop, i) => ({
            id: stop.shipmentId || `stop-${i}`,
            location: {
              lat: stop.location?.latitude ?? 0,
              lng: stop.location?.longitude ?? 0,
            },
            name: locations[i]?.name ?? `Stop ${i + 1}`,
          })) ?? [],
      };

      setRoute(optimizedRoute);
    }
  };

  return (
    <div className="app">
      <div className="sidebar">
        <h2>Route Optimization</h2>

        <div className="locations">
          {locations.map((loc, i) => (
            <div key={i} className="location-item">
              {loc.name}
            </div>
          ))}
        </div>

        <button onClick={handleOptimize} disabled={isLoading}>
          {isLoading ? 'Optimizing...' : 'Optimize Route'}
        </button>

        {isSuccess && <div className="success">✓ Route optimized successfully!</div>}
      </div>

      <div className="map-container">
        <RouteMapView
          apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY!}
          route={route}
          mapConfig={{
            center: { lat: 13.7563, lng: 100.5018 },
            zoom: 12,
          }}
        />
      </div>
    </div>
  );
}

export default CompleteApp;

TypeScript Support

All hooks are fully typed. Import types from the packages:

tsx
import type {
  Route,
  Stop,
  MapConfig,
  OptimizationRequest,
  OptimizationResponse,
} from '@route-optimization/core';

import type {
  UseRouteMapOptions,
  UseRouteMapReturn,
  UseMapControlsReturn,
  UseRouteOptimizationResult,
} from '@route-optimization/react';

Best Practices

1. Environment Variables

Store API keys in environment variables:

env
# .env.local
REACT_APP_GOOGLE_MAPS_API_KEY=your_api_key
REACT_APP_GOOGLE_PROJECT_ID=your_project_id

2. Error Handling

Always handle errors properly:

tsx
const { optimize, error } = useRouteOptimization(config);

const handleOptimize = async () => {
  try {
    const result = await optimize(request);
    // Handle success
  } catch (err) {
    // Handle error
    console.error('Optimization failed:', err);
  }
};

3. Cleanup

Components automatically clean up when unmounted, but you can manually reset:

tsx
useEffect(() => {
  return () => {
    clearRoute();
    reset();
  };
}, []);

4. Memoization

Memoize large configuration objects:

tsx
const mapConfig = useMemo(
  () => ({
    center: { lat: 13.7563, lng: 100.5018 },
    zoom: 12,
    styles: customMapStyles,
  }),
  []
);

const { mapRef } = useRouteMap({
  apiKey,
  ...mapConfig,
});

Next Steps

Released under the MIT License.