Skip to main content
تکمیل خودکار آدرس در صفحه پرداخت React: رها کردن سبد خرید را با
Tutorials

تکمیل خودکار آدرس در صفحه پرداخت React: رها کردن سبد خرید را با

با MapAtlas Geocoding API تکمیل خودکار آدرس را به صفحه پرداخت React اضافه کن. رها کردن سبد خرید را کم کن، ورود در موبایل را سریع‌تر کن و آدرس‌ها را real-time اعتبارسنجی

Brent van der Heiden21 min read
#address autocomplete#geocoding api#checkout conversion#address validation#javascript autocomplete

فیلد آدرس در صفحه پرداخت جایی است که تبدیل‌های موبایلی از دست می‌روند. کاربر به صفحه محصول می‌رود، کالا را به سبد خرید اضافه می‌کند، به پرداخت می‌رود، و ناگهان از او خواسته می‌شود آدرس کامل خود را روی صفحه‌کلید لمسی تایپ کند. اگر کد پستی را اشتباه وارد کند، فرمت شماره خانه را بد بزند، یا تسلیم شود، فروشی را که قبلاً به دست آورده بودید از دست داده‌اید.

تکمیل خودکار آدرس این مشکل را حل می‌کند. بعد از پیاده‌سازی، ورود آدرس از ۱۵ تا ۲۵ کلید به ۳ تا ۴ کلید کاهش می‌یابد. کاربر شروع به تایپ نام خیابان می‌کند، در کمتر از نیم ثانیه پیشنهادی می‌بیند، روی آن ضربه می‌زند، و تمام آدرس، خیابان، شماره خانه، شهر، کد پستی، کشور، به‌صورت خودکار و صحیح پر می‌شود. تحویل‌های ناموفق ناشی از غلط‌نویسی کاهش می‌یابد. رها کردن پرداخت کاهش می‌یابد. و مهم‌تر از همه، مختصات جغرافیایی تأیید شده به هر سفارش متصل می‌شود که سیستم‌های لجستیک و مسیریابی شما می‌توانند مستقیماً استفاده کنند.

تحقیقات در پیاده‌سازی‌های تجارت الکترونیک به‌طور مداوم ۲۵ تا ۳۵ درصد بهبود در نرخ تکمیل پرداخت پس از افزودن تکمیل خودکار آدرس نشان می‌دهد، با اثر به‌طور قابل توجهی قوی‌تر در موبایل. این آموزش یک کامپوننت کامل تکمیل خودکار آدرس در React با استفاده از MapAtlas Geocoding API می‌سازد، شامل debouncing، ناوبری صفحه‌کلید، مدیریت فرمت آدرس اتحادیه اروپا، و یکپارچه‌سازی فرم. کامپوننت کامل حدود ۹۰ خط است.

چرا خطاهای آدرس نرخ تبدیل را از بین می‌برند

تحویل‌های ناموفق از همه جهات گران‌قیمت هستند: شرکت حمل‌ونقل هزینه ارسال مجدد می‌گیرد، تیم پشتیبانی شما شکایت را پیگیری می‌کند، و اعتماد مشتری به برند شما آسیب می‌بیند. در تجارت الکترونیک B2C، خطاهای ورود آدرس حدود ۵ تا ۸ درصد از تمام استثناهای ارسال را تشکیل می‌دهند.

دلایل زمینه‌ای قابل پیش‌بینی هستند:

  • ورود با صفحه‌کلید موبایل خطاهای تایپی بیشتری نسبت به دسکتاپ ایجاد می‌کند. تصحیح خودکار اغلب نام خیابان‌ها و شهرها را خراب می‌کند.
  • فرمت کدهای پستی بر اساس کشور متفاوت است. یک مشتری آلمانی که کد ۵ رقمی را در فیلدی که فرمت انگلیسی انتظار دارد وارد می‌کند، خطای اعتبارسنجی ایجاد می‌کند.
  • ترتیب خیابان/شماره خانه در کشورهای اتحادیه اروپا متفاوت است. در آلمان و هلند، شماره خانه بعد از نام خیابان می‌آید. در فرانسه، قبل از آن. فرم‌های ورود دستی به‌ندرت کاربران را درست راهنمایی می‌کنند.
  • تعیین آپارتمان و طبقه فرمت استانداردی ندارد. کاربران آن را در هر فرمتی که طبیعی به نظر می‌رسد وارد می‌کنند، که اغلب با آنچه شرکت حمل‌ونقل شما انتظار دارد مطابقت ندارد.

تکمیل خودکار اکثر این مشکلات را دور می‌زند با بازگرداندن یک شیء آدرس از پیش اعتبارسنجی شده و ساختاریافته. کاربر چیزی را که منظور دارد انتخاب می‌کند، و فرم شما فرمت صحیح را دریافت می‌کند.

endpoint تکمیل خودکار Geocoding MapAtlas

endpoint برای پیشنهادات تکمیل خودکار:

GET https://api.mapatlas.eu/geocoding/v1/autocomplete?text={query}&key={YOUR_API_KEY}

پارامترهای اختیاری مهم برای تجارت الکترونیک اتحادیه اروپا:

پارامترنوعتوضیح
textstringکوئری آدرس ناقص
focus.point.lonnumberطول جغرافیایی کاربر (نتایج نزدیک را اولویت‌بندی می‌کند)
focus.point.latnumberعرض جغرافیایی کاربر (نتایج نزدیک را اولویت‌بندی می‌کند)
boundary.countrystringکد کشور ISO 3166-1 alpha-3 (مثلاً DEU، FRA، NLD)
layersstringفیلتر انواع نتیجه: address، street، locality
sizenumberتعداد نتایج (پیش‌فرض ۱۰، حداکثر ۲۰)

یک پاسخ نمونه:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": { "type": "Point", "coordinates": [4.9041, 52.3676] },
      "properties": {
        "id": "address:node/1234567",
        "label": "Damrak 1, 1012 LG Amsterdam, Netherlands",
        "name": "Damrak 1",
        "street": "Damrak",
        "housenumber": "1",
        "postalcode": "1012 LG",
        "locality": "Amsterdam",
        "region": "North Holland",
        "country": "Netherlands",
        "country_code": "NL",
        "confidence": 0.98
      }
    }
  ]
}

هر نتیجه به‌عنوان یک feature در GeoJSON با اجزای آدرس ساختاریافته برمی‌گردد. فرم شما داده‌های تمیز و اعتبارسنجی‌شده دریافت می‌کند که می‌توانید مستقیماً در هر فیلد وارد کنید، یا به‌عنوان یک شیء واحد همراه با مختصات برای برنامه‌ریزی مسیریابی و تحویل ذخیره کنید.

ساختن React Autocomplete Hook

با استخراج منطق API به یک hook قابل استفاده مجدد شروع کنید. این کامپوننت را تمیز نگه می‌دارد و hook را به‌طور مستقل قابل آزمون می‌کند.

// hooks/useAddressAutocomplete.js
import { useState, useEffect, useRef } from 'react';

const API_BASE = 'https://api.mapatlas.eu/geocoding/v1/autocomplete';
const API_KEY  = process.env.NEXT_PUBLIC_MAPATLAS_KEY;
const DEBOUNCE_MS = 300;
const MIN_CHARS   = 3;

export function useAddressAutocomplete(countryCode = null) {
  const [query,       setQuery]       = useState('');
  const [suggestions, setSuggestions] = useState([]);
  const [loading,     setLoading]     = useState(false);
  const [error,       setError]       = useState(null);
  const debounceTimer = useRef(null);

  useEffect(() => {
    if (query.length < MIN_CHARS) {
      setSuggestions([]);
      return;
    }

    clearTimeout(debounceTimer.current);

    debounceTimer.current = setTimeout(async () => {
      setLoading(true);
      setError(null);

      try {
        const url = new URL(API_BASE);
        url.searchParams.set('text', query);
        url.searchParams.set('key', API_KEY);
        url.searchParams.set('size', '6');
        url.searchParams.set('layers', 'address');
        if (countryCode) {
          url.searchParams.set('boundary.country', countryCode);
        }

        const res  = await fetch(url.toString());
        if (!res.ok) throw new Error(`API error: ${res.status}`);

        const data = await res.json();
        setSuggestions(data.features ?? []);
      } catch (err) {
        setError(err.message);
        setSuggestions([]);
      } finally {
        setLoading(false);
      }
    }, DEBOUNCE_MS);

    return () => clearTimeout(debounceTimer.current);
  }, [query, countryCode]);

  return { query, setQuery, suggestions, loading, error };
}

تایمر debounce فقط پس از اینکه کاربر ۳۰۰ میلی‌ثانیه از تایپ متوقف شد فعال می‌شود. محافظ MIN_CHARS از فراخوانی API با ورودی‌های ۱ تا ۲ کاراکتری جلوگیری می‌کند. هر دو اقدام برای نگه داشتن مصرف API متناسب با قصد واقعی کاربر ضروری هستند.

کامپوننت Autocomplete

// components/AddressAutocomplete.jsx
import { useState, useRef } from 'react';
import { useAddressAutocomplete } from '../hooks/useAddressAutocomplete';

export function AddressAutocomplete({ onSelect, countryCode, placeholder }) {
  const { query, setQuery, suggestions, loading } = useAddressAutocomplete(countryCode);
  const [open,        setOpen]    = useState(false);
  const [highlighted, setHighlighted] = useState(-1);
  const inputRef = useRef(null);

  function handleSelect(feature) {
    const p = feature.properties;
    setQuery(p.label);
    setOpen(false);
    setHighlighted(-1);
    onSelect({
      label:       p.label,
      street:      p.street      ?? '',
      housenumber: p.housenumber ?? '',
      postalcode:  p.postalcode  ?? '',
      locality:    p.locality    ?? '',
      region:      p.region      ?? '',
      country:     p.country     ?? '',
      country_code: p.country_code ?? '',
      coordinates: feature.geometry.coordinates, // [lng, lat]
    });
  }

  function handleKeyDown(e) {
    if (!open || suggestions.length === 0) return;
    if (e.key === 'ArrowDown')  setHighlighted(h => Math.min(h + 1, suggestions.length - 1));
    if (e.key === 'ArrowUp')    setHighlighted(h => Math.max(h - 1, 0));
    if (e.key === 'Enter' && highlighted >= 0) handleSelect(suggestions[highlighted]);
    if (e.key === 'Escape')     setOpen(false);
  }

  return (
    <div style={{ position: 'relative' }}>
      <input
        ref={inputRef}
        type="text"
        value={query}
        placeholder={placeholder ?? 'Start typing your address...'}
        onChange={e => { setQuery(e.target.value); setOpen(true); setHighlighted(-1); }}
        onKeyDown={handleKeyDown}
        onBlur={() => setTimeout(() => setOpen(false), 150)}
        style={{ width: '100%', padding: '10px 12px', fontSize: 16, borderRadius: 6, border: '1px solid #ccc' }}
        autoComplete="off"
        aria-autocomplete="list"
        aria-haspopup="listbox"
        aria-expanded={open && suggestions.length > 0}
      />

      {loading && (
        <span style={{ position: 'absolute', right: 12, top: '50%', transform: 'translateY(-50%)', fontSize: 12, color: '#888' }}>
          Searching…
        </span>
      )}

      {open && suggestions.length > 0 && (
        <ul
          role="listbox"
          style={{
            position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 999,
            background: '#fff', border: '1px solid #ccc', borderTop: 'none',
            borderRadius: '0 0 6px 6px', listStyle: 'none', margin: 0, padding: 0,
            boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
          }}
        >
          {suggestions.map((feature, i) => (
            <li
              key={feature.properties.id}
              role="option"
              aria-selected={i === highlighted}
              onMouseDown={() => handleSelect(feature)}
              onMouseEnter={() => setHighlighted(i)}
              style={{
                padding: '10px 12px',
                cursor: 'pointer',
                fontSize: 14,
                background: i === highlighted ? '#f0f7e6' : '#fff',
                borderBottom: i < suggestions.length - 1 ? '1px solid #f0f0f0' : 'none',
              }}
            >
              {feature.properties.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

کامپوننت ناوبری کامل صفحه‌کلید (کلیدهای جهت‌دار، Enter، Escape)، ویژگی‌های ARIA برای سازگاری با خواننده صفحه، و تأخیر ۱۵۰ میلی‌ثانیه‌ای blur را مدیریت می‌کند تا کلیک‌های موس روی پیشنهادات قبل از بسته شدن لیست ثبت شوند.

یکپارچه‌سازی با فرم پرداخت

// pages/checkout.jsx
import { useState } from 'react';
import { AddressAutocomplete } from '../components/AddressAutocomplete';

export default function CheckoutPage() {
  const [address, setAddress] = useState({
    street: '', housenumber: '', postalcode: '',
    locality: '', country: '', coordinates: null,
  });

  function handleAddressSelect(selected) {
    setAddress(selected);
    // Coordinates are available for routing/delivery estimation
    console.log('Delivery coordinates:', selected.coordinates);
  }

  return (
    <form>
      <h2>Delivery address</h2>

      <AddressAutocomplete
        onSelect={handleAddressSelect}
        countryCode="NLD"  // Restrict to Netherlands, remove for EU-wide
        placeholder="Start typing your street address..."
      />

      {/* Show structured fields after selection, allow manual edits */}
      {address.street && (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 8, marginTop: 12 }}>
          <input value={address.street}      onChange={e => setAddress(a => ({ ...a, street: e.target.value }))}      placeholder="Street" />
          <input value={address.housenumber} onChange={e => setAddress(a => ({ ...a, housenumber: e.target.value }))} placeholder="No." style={{ width: 80 }} />
          <input value={address.postalcode}  onChange={e => setAddress(a => ({ ...a, postalcode: e.target.value }))}  placeholder="Postal code" />
          <input value={address.locality}    onChange={e => setAddress(a => ({ ...a, locality: e.target.value }))}    placeholder="City" />
        </div>
      )}

      <button type="submit" style={{ marginTop: 16 }}>
        Continue to payment
      </button>
    </form>
  );
}

نمایش فیلدهای قابل ویرایش جداگانه پس از تکمیل خودکار برای دسترسی‌پذیری و موارد خاص مهم است. آدرس در واقعیت ممکن است شامل شماره آپارتمان یا کد دسترسی باشد که نتیجه geocode شامل آن نمی‌شود. تکمیل خودکار آدرس پایه اعتبارسنجی‌شده را پر می‌کند؛ کاربر بقیه را اضافه می‌کند.

ملاحظات فرمت آدرس اتحادیه اروپا

کشورهای مختلف اتحادیه اروپا قراردادهای آدرس متفاوتی دارند که هم نمایش و هم ترتیب فیلدهای فرم را تحت تأثیر قرار می‌دهند:

آلمان (DEU): ابتدا خیابان، سپس شماره خانه. Hauptstraße 42, 10115 Berlin. ویژگی housenumber از API به‌درستی بعد از خیابان در نتایج آلمانی می‌آید.

فرانسه (FRA): شماره خانه قبل از خیابان. 42 rue de Rivoli, 75001 Paris. ویژگی label آدرس‌ها را در فرمت مناسب کشور برمی‌گرداند.

هلند (NLD): کدهای پستی هلندی ۴ رقم به علاوه ۲ حرف بزرگ با فاصله هستند: 1012 LG. اگر کد پستی را برای سیستم ارسال خود تقسیم می‌کنید، این فرمت را اعتبارسنجی کنید.

بلژیک (BEL): مناطق دوزبانه ممکن است آدرس‌ها را بسته به شهرداری به فرانسوی یا هلندی برگردانند.

MapAtlas Geocoding API همه اینها را به‌درستی در فیلد label مدیریت می‌کند، در حالی که فیلدهای ساختاریافته street، housenumber و postalcode را نیز برمی‌گرداند تا در صورت نیاز بتوانید طرح‌بندی‌های فرم خاص کشور بسازید.

برای اعتبارسنجی انبوه پایگاه‌های داده آدرس موجود، مثلاً پاک‌سازی یک CRM قدیمی قبل از راه‌اندازی سرویس تحویل، به نحوه استفاده از Geocoding API برای اعتبارسنجی ۱۰,۰۰۰ آدرس به‌صورت انبوه مراجعه کنید.

ملاحظات عملکردی

پیاده‌سازی بالا به‌طور متوسط تقریباً یک فراخوانی API برای هر ۳ تا ۴ کاراکتر تایپ شده انجام می‌دهد (با debounce 300 میلی‌ثانیه‌ای که تایپ سریع را جذب می‌کند). برای یک سایت تجارت الکترونیک با حجم پرداخت قابل توجه، یک پروکسی سمت سرور در جلوی Geocoding API قرار دهید تا API key شما هرگز در کد سمت کلاینت نمایش داده نشود:

// pages/api/autocomplete.js (Next.js API route)
export default async function handler(req, res) {
  const { text, countryCode } = req.query;
  const url = new URL('https://api.mapatlas.eu/geocoding/v1/autocomplete');
  url.searchParams.set('text',  text);
  url.searchParams.set('key',   process.env.MAPATLAS_KEY); // Server-side env var
  url.searchParams.set('size',  '6');
  url.searchParams.set('layers', 'address');
  if (countryCode) url.searchParams.set('boundary.country', countryCode);

  const response = await fetch(url.toString());
  const data     = await response.json();
  res.json(data);
}

سپس hook را به‌روزرسانی کنید تا به جای MapAtlas API مستقیم، به /api/autocomplete فراخوانی کند. این رویکرد همچنین به شما امکان می‌دهد کش درخواست را در لایه edge اضافه کنید تا فراخوانی‌های API را برای کوئری‌های رایج کاهش دهید.

خلاصه

یک فیلد تکمیل خودکار آدرس می‌تواند نرخ تبدیل پرداخت شما را به‌طور معناداری بهبود بخشد. پیاده‌سازی ساده است: یک fetch با debounce به MapAtlas Geocoding API، یک کامپوننت dropdown کوچک با ناوبری صفحه‌کلید، و یکپارچه‌سازی فرم که فیلدهای ساختاریافته را از نتیجه انتخاب‌شده پر می‌کند.

تصمیم‌های کلیدی:

  • Debounce در ۳۰۰ میلی‌ثانیه برای جلوگیری از فراخوانی‌های API بیش از حد.
  • نیاز به ۳ کاراکتر قبل از فعال کردن درخواست‌ها.
  • محدود کردن به کشور اگر جغرافیای پایه کاربران خود را می‌دانید، این کار ارتباط نتایج را به‌طور قابل توجهی بهبود می‌بخشد.
  • همیشه ویرایش دستی فیلدهای تکمیل خودکار شده را برای شماره‌های آپارتمان، کدهای دسترسی و تصحیحات مجاز کنید.
  • API key را سمت سرور پروکسی کنید تا از نمایش اعتبارنامه‌ها در بسته‌های کلاینت جلوگیری شود.

برای اولین یکپارچه‌سازی نقشه در کنار فیلد آدرس، به نحوه افزودن نقشه‌های تعاملی به وب‌سایت شما مراجعه کنید تا مکان تحویل را در صفحه تأیید نشان دهید.


برای شروع ساخت، یک API key رایگان MapAtlas دریافت کنید. Geocoding API در سطح رایگان گنجانده شده، بدون نیاز به کارت اعتباری. فیلد آدرس پرداخت جایی است که تبدیل‌های موبایل به آنجا می‌روند و از بین می‌روند. کاربری صفحه محصول شما را بازدید می‌کند، یک کالا به سبد خرید اضافه می‌کند، به صفحه پرداخت می‌رود، و سپس از او خواسته می‌شود که آدرس خیابان کامل خود را در صفحه‌کلید لمسی 6 اینچی تایپ کند. اگر کد پستی را غلط تایپ کند، قالب شماره خانه را اشتباه بگیرد یا به سادگی تسلیم شود، یک فروشی را که از قبل برنده شده‌اید از دست خواهید داد.

تکمیل خودکار آدرس این مشکل را حل می‌کند. پس از پیاده‌سازی، ورود آدرس از 15 تا 25 کلید‌زنی به 3 تا 4 کاهش می‌یابد. کاربر شروع به تایپ نام خیابان می‌کند، در مدت نیم ثانیه پیشنهادی مطابق می‌بیند، روی آن ضربه می‌زند، و کل آدرس شامل خیابان، شماره خانه، شهر، کد پستی و کشور به طور خودکار و صحیح پرشده می‌شود. تحویل‌های ناموفق ناشی از اشتباهات تایپی کاهش می‌یابد. رها کردن پرداخت کاهش می‌یابد. و مهمتر از همه، شما مختصات تأیید‌شده و ژئوکد شده‌ای دارید که به هر سفارش متصل است، که سیستم‌های لجستیکی و مسیریابی شما می‌توانند مستقیماً از آن استفاده کنند.

تحقیقات در سراسر پیاده‌سازی‌های تجارت الکترونیکی به طور مداوم نشان می‌دهد که پس از افزودن تکمیل خودکار آدرس، 25 تا 35 درصد بهبود در نرخ تکمیل پرداخت وجود دارد، اثر آن به طور قابل‌توجهی در موبایل قوی‌تر است، جایی که ورود متن دستی کندترین و پرخطرترین است. برخی از پیاده‌سازی‌های هدفمند برای بازارهای موبایل‌محور کل 35 درصد را گزارش می‌کنند.

این آموزش یک کامپوننت تکمیل خودکار آدرس React کامل را با استفاده از MapAtlas Geocoding API، شامل debounce، ناوبری صفحه‌کلید، مدیریت قالب آدرس اروپایی و یکپارچگی فرم می‌سازد. کامپوننت کامل حدود 90 خط است.

چرا خطاهای آدرس تبدیل‌ها را می‌کشند

تحویل‌های ناموفق در هر جهت مهم است: حامل هزینه تحویل مجدد را شارژ می‌کند، تیم خدمات مشتری شما از شکایت رسیدگی می‌کند، و اعتماد مشتری به برند شما ضربه می‌خورد. در تجارت الکترونیکی B2C، خطاهای ورود آدرس حدود 5 تا 8 درصد از تمام استثناهای حمل را نشان می‌دهند.

علل بنیادی قابل‌پیش‌بینی هستند:

  • ورود از طریق صفحه‌کلید موبایل اشتباهات تایپی بیشتری نسبت به دسک‌تاپ ایجاد می‌کند. خودتصحیح اغلب نام‌های خیابان و شهر را خراب می‌کند.
  • قالب‌های کد پستی در هر کشور متفاوت است. مشتری آلمانی که کد 5 رقمی را در فیلدی برای قالب انگلیسی (AN NAA) وارد می‌کند، خطای اعتبارسنجی را فعال می‌کند.
  • ترتیب خیابان و شماره خانه در سراسر کشورهای اروپایی متفاوت است. در آلمان و هلند، شماره خانه از خیابان پیروی می‌کند. در فرانسه، از آن جلوتر است. فرم‌های ورود دستی به ندرت کاربران را به درستی راهنمایی می‌کنند.
  • نام‌های آپارتمان و طبقه هیچ قالب استانداردی ندارند. کاربران آنها را در هر قالبی که طبیعی به نظر می‌رسد وارد می‌کنند، که اغلب با آن‌چه حامل حمل و نقل شما انتظار دارد منطبق نیست.

تکمیل خودکار اکثر این مشکلات را دور می‌زند و یک شی آدرس از پیش تأیید‌شده و ساختارشده‌ای بازمی‌گرداند. کاربر آن‌چه را قصد دارد انتخاب می‌کند، و فرم شما قالب صحیح را دریافت می‌کند.

نقطه‌پایانی تکمیل خودکار Geocoding شامل MapAtlas

نقطه‌پایانی برای پیشنهادات تکمیل خودکار:

GET https://api.mapatlas.eu/geocoding/v1/autocomplete?text={query}&key={YOUR_API_KEY}

پارامترهای اختیاری که برای تجارت الکترونیکی اروپایی اهمیت دارند:

پارامترنوعتوضیح
textstringجستجوی آدرس جزئی
focus.point.lonnumberطول جغرافیایی کاربر (نتایج نزدیک را ترجیح می‌دهد)
focus.point.latnumberعرض جغرافیایی کاربر (نتایج نزدیک را ترجیح می‌دهد)
boundary.countrystringکد کشور ISO 3166-1 alpha-3 (مثلاً DEU, FRA, NLD)
layersstringفیلتر انواع نتیجه: address, street, locality
sizenumberتعداد نتایج (پیش‌فرض 10، حداکثر 20)

یک پاسخ معمولی:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": { "type": "Point", "coordinates": [4.9041, 52.3676] },
      "properties": {
        "id": "address:node/1234567",
        "label": "Damrak 1, 1012 LG Amsterdam, Netherlands",
        "name": "Damrak 1",
        "street": "Damrak",
        "housenumber": "1",
        "postalcode": "1012 LG",
        "locality": "Amsterdam",
        "region": "North Holland",
        "country": "Netherlands",
        "country_code": "NL",
        "confidence": 0.98
      }
    }
  ]
}

هر نتیجه به عنوان یک ویژگی GeoJSON با اجزای آدرس ساختارشده بازمی‌گردد. فرم شما داده‌های تمیز و تأیید‌شده‌ای دریافت می‌کند که می‌توانید آنها را مستقیماً به هر فیلد وارد کنید، یا به عنوان یک شی واحد در کنار مختصات برای مسیریابی و برنامه‌ریزی تحویل ذخیره کنید.

ساخت React Autocomplete Hook

با استخراج منطق API به یک hook قابل استفاده مجدد شروع کنید. این کامپوننت را تمیز نگاه می‌دارد و hook را قابل آزمایش می‌کند.

// hooks/useAddressAutocomplete.js
import { useState, useEffect, useRef } from 'react';

const API_BASE = 'https://api.mapatlas.eu/geocoding/v1/autocomplete';
const API_KEY  = process.env.NEXT_PUBLIC_MAPATLAS_KEY;
const DEBOUNCE_MS = 300;
const MIN_CHARS   = 3;

export function useAddressAutocomplete(countryCode = null) {
  const [query,       setQuery]       = useState('');
  const [suggestions, setSuggestions] = useState([]);
  const [loading,     setLoading]     = useState(false);
  const [error,       setError]       = useState(null);
  const debounceTimer = useRef(null);

  useEffect(() => {
    if (query.length < MIN_CHARS) {
      setSuggestions([]);
      return;
    }

    clearTimeout(debounceTimer.current);

    debounceTimer.current = setTimeout(async () => {
      setLoading(true);
      setError(null);

      try {
        const url = new URL(API_BASE);
        url.searchParams.set('text', query);
        url.searchParams.set('key', API_KEY);
        url.searchParams.set('size', '6');
        url.searchParams.set('layers', 'address');
        if (countryCode) {
          url.searchParams.set('boundary.country', countryCode);
        }

        const res  = await fetch(url.toString());
        if (!res.ok) throw new Error(`API error: ${res.status}`);

        const data = await res.json();
        setSuggestions(data.features ?? []);
      } catch (err) {
        setError(err.message);
        setSuggestions([]);
      } finally {
        setLoading(false);
      }
    }, DEBOUNCE_MS);

    return () => clearTimeout(debounceTimer.current);
  }, [query, countryCode]);

  return { query, setQuery, suggestions, loading, error };
}

تایمر debounce فقط پس از توقف کاربر برای 300 میلی‌ثانیه کار می‌کند. محافظ MIN_CHARS تماس‌های API را برای ورودی‌های 1 تا 2 کاراکتری جایی که نتایج خیلی گسترده خواهند بود جلوگیری می‌کند. هر دو اقدام برای نگه‌داشتن استفاده از API و هزینه‌ها متناسب با نیت واقعی کاربر حیاتی هستند.

کامپوننت تکمیل خودکار

// components/AddressAutocomplete.jsx
import { useState, useRef } from 'react';
import { useAddressAutocomplete } from '../hooks/useAddressAutocomplete';

export function AddressAutocomplete({ onSelect, countryCode, placeholder }) {
  const { query, setQuery, suggestions, loading } = useAddressAutocomplete(countryCode);
  const [open,        setOpen]    = useState(false);
  const [highlighted, setHighlighted] = useState(-1);
  const inputRef = useRef(null);

  function handleSelect(feature) {
    const p = feature.properties;
    setQuery(p.label);
    setOpen(false);
    setHighlighted(-1);
    onSelect({
      label:       p.label,
      street:      p.street      ?? '',
      housenumber: p.housenumber ?? '',
      postalcode:  p.postalcode  ?? '',
      locality:    p.locality    ?? '',
      region:      p.region      ?? '',
      country:     p.country     ?? '',
      country_code: p.country_code ?? '',
      coordinates: feature.geometry.coordinates, // [lng, lat]
    });
  }

  function handleKeyDown(e) {
    if (!open || suggestions.length === 0) return;
    if (e.key === 'ArrowDown')  setHighlighted(h => Math.min(h + 1, suggestions.length - 1));
    if (e.key === 'ArrowUp')    setHighlighted(h => Math.max(h - 1, 0));
    if (e.key === 'Enter' && highlighted >= 0) handleSelect(suggestions[highlighted]);
    if (e.key === 'Escape')     setOpen(false);
  }

  return (
    <div style={{ position: 'relative' }}>
      <input
        ref={inputRef}
        type="text"
        value={query}
        placeholder={placeholder ?? 'Start typing your address...'}
        onChange={e => { setQuery(e.target.value); setOpen(true); setHighlighted(-1); }}
        onKeyDown={handleKeyDown}
        onBlur={() => setTimeout(() => setOpen(false), 150)}
        style={{ width: '100%', padding: '10px 12px', fontSize: 16, borderRadius: 6, border: '1px solid #ccc' }}
        autoComplete="off"
        aria-autocomplete="list"
        aria-haspopup="listbox"
        aria-expanded={open && suggestions.length > 0}
      />

      {loading && (
        <span style={{ position: 'absolute', right: 12, top: '50%', transform: 'translateY(-50%)', fontSize: 12, color: '#888' }}>
          Searching…
        </span>
      )}

      {open && suggestions.length > 0 && (
        <ul
          role="listbox"
          style={{
            position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 999,
            background: '#fff', border: '1px solid #ccc', borderTop: 'none',
            borderRadius: '0 0 6px 6px', listStyle: 'none', margin: 0, padding: 0,
            boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
          }}
        >
          {suggestions.map((feature, i) => (
            <li
              key={feature.properties.id}
              role="option"
              aria-selected={i === highlighted}
              onMouseDown={() => handleSelect(feature)}
              onMouseEnter={() => setHighlighted(i)}
              style={{
                padding: '10px 12px',
                cursor: 'pointer',
                fontSize: 14,
                background: i === highlighted ? '#f0f7e6' : '#fff',
                borderBottom: i < suggestions.length - 1 ? '1px solid #f0f0f0' : 'none',
              }}
            >
              {feature.properties.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

کامپوننت ناوبری صفحه‌کلید کامل (کلیدهای فلش، Enter، Escape)، ویژگی‌های ARIA برای سازگاری با صفحه‌خوان، و تأخیر blur 150 میلی‌ثانیه تا کلیک‌های موشی بر پیشنهادات قبل از بسته شدن لیست ثبت شوند را مدیریت می‌کند.

یکپارچگی با فرم پرداخت

// pages/checkout.jsx
import { useState } from 'react';
import { AddressAutocomplete } from '../components/AddressAutocomplete';

export default function CheckoutPage() {
  const [address, setAddress] = useState({
    street: '', housenumber: '', postalcode: '',
    locality: '', country: '', coordinates: null,
  });

  function handleAddressSelect(selected) {
    setAddress(selected);
    // Coordinates are available for routing/delivery estimation
    console.log('Delivery coordinates:', selected.coordinates);
  }

  return (
    <form>
      <h2>Delivery address</h2>

      <AddressAutocomplete
        onSelect={handleAddressSelect}
        countryCode="NLD"  // Restrict to Netherlands, remove for EU-wide
        placeholder="Start typing your street address..."
      />

      {/* Show structured fields after selection, allow manual edits */}
      {address.street && (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 8, marginTop: 12 }}>
          <input value={address.street}      onChange={e => setAddress(a => ({ ...a, street: e.target.value }))}      placeholder="Street" />
          <input value={address.housenumber} onChange={e => setAddress(a => ({ ...a, housenumber: e.target.value }))} placeholder="No." style={{ width: 80 }} />
          <input value={address.postalcode}  onChange={e => setAddress(a => ({ ...a, postalcode: e.target.value }))}  placeholder="Postal code" />
          <input value={address.locality}    onChange={e => setAddress(a => ({ ...a, locality: e.target.value }))}    placeholder="City" />
        </div>
      )}

      <button type="submit" style={{ marginTop: 16 }}>
        Continue to payment
      </button>
    </form>
  );
}

نمایش فیلدهای ویرایش‌پذیر جداگانه پس از تکمیل خودکار برای دسترسی‌پذیری و موارد لبه‌ای مهم است. آدرس واقعی درب کاربر ممکن است شامل شماره آپارتمان یا کد دسترسی باشد که نتیجه ژئوکد شده شامل نمی‌شود. تکمیل خودکار آدرس پایه تأیید‌شده را پر می‌کند. کاربر بقیه را اضافه می‌کند.

ملاحظات قالب آدرس اروپایی

کشورهای اروپایی مختلف دارای قراردادهای آدرس هستند که بر ترتیب نمایش و فیلد فرم تأثیر می‌گذارند:

آلمان (DEU): خیابان اول، شماره خانه بعد. Hauptstraße 42, 10115 Berlin. ویژگی housenumber از API به درستی از خیابان در نتایج آلمانی پیروی می‌کند.

فرانسه (FRA): شماره خانه قبل از خیابان. 42 rue de Rivoli, 75001 Paris. ویژگی label آدرس‌ها را در قالب مناسب برای کشور بازمی‌گرداند.

هلند (NLD): کدهای پستی هلندی 4 رقم + 2 حرف بزرگ با فاصله هستند: 1012 LG. اگر کد پستی را برای سیستم حمل و نقل خود تقسیم می‌کنید، این قالب را تأیید کنید.

بلژیک (BEL): مناطق دوزبانی ممکن است آدرس‌ها را به فرانسوی یا هلندی بسته‌ای بر روی شهرداری بازگردانند.

MapAtlas Geocoding API تمام این‌ها را به درستی در فیلد label (خوانا، مناسب برای کشور) مدیریت می‌کند، در حالی که فیلدهای ساختارشده street, housenumber و postalcode را نیز بازمی‌گرداند تا بتوانید طرح‌های فرم مخصوص کشور بسازید.

برای اعتبارسنجی دسته‌ای پایگاه‌های داده آدرس موجود، شاید پاک‌سازی CRM قدیمی قبل از راه‌اندازی سرویس تحویل، How to Use the Geocoding API to Validate 10,000 Addresses in Bulk را ببینید.

ملاحظات کارایی

اجرای فوق تقریباً یک تماس API در 3 تا 4 کاراکتر تایپ شده به‌طور میانگین می‌سازد (با debounce 300 میلی‌ثانیه که تایپ سریع را جذب می‌کند). برای سایت تجارت الکترونیکی با حجم پرداخت معنادار، یک proxy سمت سرور را در جلوی Geocoding API تنظیم کنید تا کلید API شما هرگز در کد سمت کلاینت ظاهر نشود:

// pages/api/autocomplete.js (Next.js API route)
export default async function handler(req, res) {
  const { text, countryCode } = req.query;
  const url = new URL('https://api.mapatlas.eu/geocoding/v1/autocomplete');
  url.searchParams.set('text',  text);
  url.searchParams.set('key',   process.env.MAPATLAS_KEY); // Server-side env var
  url.searchParams.set('size',  '6');
  url.searchParams.set('layers', 'address');
  if (countryCode) url.searchParams.set('boundary.country', countryCode);

  const response = await fetch(url.toString());
  const data     = await response.json();
  res.json(data);
}

سپس hook را به‌روزرسانی کنید تا به جای اینکه مستقیماً به API MapAtlas تماس بگیرید، /api/autocomplete را صدا بزند. این رویکرد شما را قادر می‌سازد که درخواست‌های ذخیره‌سازی در لایه edge (Vercel Edge Functions، Cloudflare Workers) را اضافه کنید تا تماس‌های API را برای جستجوهای رایج کاهش دهید.

برای نرخ‌های API Geocoding و حدود سطح رایگان فعلی MapAtlas Pricing page را ببینید، برای اکثر پیاده‌سازی‌های تجارت الکترونیکی، استفاده از تکمیل خودکار در سطح رایگان در طول توسعه راحت است.

خلاصه

یک فیلد تکمیل خودکار آدرس می‌تواند به‌طور معنی‌داری نرخ تبدیل پرداخت شما را حرکت دهد. اجرا ساده است: یک واکشی debounce شده به MapAtlas Geocoding API، یک کامپوننت dropdown کوچک با ناوبری صفحه‌کلید، و یکپارچگی فرم که فیلدهای ساختارشده را از نتیجه انتخاب‌شده پر می‌کند.

تصمیمات کلیدی:

  • Debounce در 300 میلی‌ثانیه تا از تماس‌های API بیش‌ازحد برای تایپ‌کنندگان سریع جلوگیری کنید.
  • نیاز به 3 کاراکتر قبل از آغاز درخواست‌ها.
  • محدود کردن بر اساس کشور اگر جغرافیای پایگاه کاربری خود را می‌دانید، ارتبط نتایج را به طور قابل‌توجهی بهبود می‌بخشد.
  • همیشه ویرایش دستی را مجاز کنید فیلدهای تکمیل‌شده خودکار برای شماره‌های آپارتمان، کدهای دسترسی و اصلاحات.
  • کلید API را سمت سرور proxy کنید برای تولید تا از در معرض دید قرار گرفتن تأیید‌اعتبار در بسته‌های کلاینت جلوگیری کنید.

برای اولین یکپارچگی نقشه در کنار فیلد آدرس، How to Add Interactive Maps to Your Website را ببینید تا مکان تحویل را در صفحه تأیید نشان دهید.


Sign up for a free MapAtlas API key to start building. The Geocoding API is included in the free tier, no credit card required.

سوالات متداول

تکمیل خودکار آدرس چطور نرخ تبدیل پرداخت را بهتر می‌کند؟

ورود آدرس پرتکاک‌ترین مرحله در اکثر فرآیندهای پرداخت است، خصوصاً روی موبایل. تکمیل خودکار آن را به ۲-۳ ضربه کلید و یک tap تقلیل می‌دهد، خطاهای فرمت که باعث شکست تحویل می‌شوند را حذف می‌کند و نگرانی از اشتباه وارد کردن آدرس را برطرف می‌کند. مطالعات به‌طور مداوم نشان می‌دهند که پس از پیاده‌سازی تکمیل خودکار، رها کردن پرداخت ۲۵ تا ۳۵ درصد کاهش می‌یابد.

آیا MapAtlas Geocoding API فرمت‌های آدرس اروپایی را پشتیبانی می‌کند؟

بله. این API فرمت‌های خاص اتحادیه اروپا را مدیریت می‌کند؛ از جمله ترتیب شماره خانه بعد از نام خیابان در آلمان، arrondissement‌های فرانسه، کدهای پستی ۴ رقمی هلند، و فرمت‌های آدرس چندزبانه در تمام کشورهای عضو اتحادیه. نتایج به‌صورت GeoJSON با اجزای آدرس ساختاریافته برگردانده می‌شوند.

چطور از فراخوانی‌های بیش از حد API در هنگام تکمیل خودکار جلوگیری کنم؟

با ۲۵۰ تا ۳۰۰ میلی‌ثانیه debounce روی input handler، فقط بعد از توقف تایپ کاربر request بفرست. همچنین یک حداقل آستانه کاراکتر (۳-۴ کاراکتر) قبل از ارسال request تنظیم کن. این دو اقدام در مقایسه با فراخوانی در هر ضربه کلید، تعداد فراخوانی‌های API را حدود ۸۰ درصد کاهش می‌دهد.

این مفید بود؟ آن را به اشتراک بگذارید.

درباره نویسنده

Brent van der Heiden

نوشته

Brent van der Heiden

Co-Founder & CEO at MapAtlas

Brent built MapAtlas out of a conviction that developers deserve location APIs with fair pricing and genuine end-user privacy. He writes about geospatial infrastructure, AI search visibility, and how location data powers the products people rely on every day.

مشاهده همه مقالات
بازگشت به وبلاگ