Building an Autocomplete Address Form in React 🗺️📍
Tags: react, googlemaps, javascript, tutorial
Published: 2024-01-04 15:36:47

As developers, creating a seamless user experience is crucial, especially when it comes to forms. Autocompleting address fields can significantly enhance the user interaction by reducing input errors and speeding up the form-filling process. In this article, we'll walk through the steps to create a React address form component that leverages a reverse geocoding API. We'll use the Google Maps API as an example, and then explore an affordable alternative using ExoAPI.

## Prerequisites

Before we dive into coding, make sure you have [Node.js](https://nodejs.org/en) and npm installed on your machine. You'll also need a basic understanding of React.

## Setting up the React App

Start by creating a new React app using Create React App:

```bash
npx create-react-app address-form-app
cd address-form-app
```

Now, let's install additional dependencies to use Google Maps API:

```bash
npm install react-geocode
```

## Reverse Geocoding

When requesting the current location from devices like computers or smartphones, we only get geographic coordinates in the form of latitude and longitude numbers. To get the actual address in a human-readable form with street name, city, postcode and country, we need to use a process called **reverse geocoding**.

Reverse geocoding is the transformation of coordinates (latitude and longitude) into a human-readable address. It works by looking up the address in a giant database of coordinates, which is why we need to use an API to query that information.

## Option A - Using Google Maps API

⚠️ If using Google APIs is not an option for you, we'll offer an simpler and cheaper alternative below using ExoAPI.

### 1. Get an API Key

Go to the [Google Cloud Console](https://console.cloud.google.com/), create a new project, and enable the "Geocoding API" for your project. Generate an API key.

### 2. Create the AddressForm Component

Create a new file `AddressForm.js` in the `src` folder:

```jsx
// src/AddressForm.js
import { useState } from "react";
import { setDefaults, fromLatLng } from "react-geocode";

setDefaults({
  // Put your Google Maps API key here:
  key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
});

const AddressForm = () => {
  const [addressComponents, setAddress] = useState({
    street: "",
    city: "",
    postcode: "",
    country: "",
    // Add more fields as needed
  });

  const useCurrentLocation = async () => {
    if (!navigator.geolocation) {
      console.error("Geolocation is not supported by this browser.");
      return;
    }

    try {
      // 1. Request current position from device
      const position = await new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject));
      const { latitude, longitude } = position.coords;

      // 2. Query the reverse geocoding API
      const address = await fromLatLng(latitude, longitude);
      console.log("[useCurrentLocation]", { latitude, longitude, address });

      // 3. Extract address components from Google Maps API response
      const components = address.results[0]?.address_components.reduce((acc, component) => {
        acc[component.types[0]] = component.long_name;
        return acc;
      }, {});

      // 4. Autocomplete form
      setAddress({
        street: [components.route, components.street_number].filter((x) => x).join(" "),
        city: components.locality || components.administrative_area_level_1 || "",
        postcode: components.postal_code || "",
        country: components.country || "",
      });
    } catch (error) {
      console.error("Error:", error);
    }
  };

  return (
    <div>
      <h2>Address Form</h2>
      <form>
        <label>
          Street:
          <input type="text" value={addressComponents.street} onChange={(e) => setAddress({ ...addressComponents, street: e.target.value })} />
        </label>
        <br />
        <label>
          City:
          <input type="text" value={addressComponents.city} onChange={(e) => setAddress({ ...addressComponents, city: e.target.value })} />
        </label>
        <br />
        <label>
          Postcode:
          <input type="text" value={addressComponents.postcode} onChange={(e) => setAddress({ ...addressComponents, postcode: e.target.value })} />
        </label>
        <br />
        <label>
          Country:
          <input type="text" value={addressComponents.country} onChange={(e) => setAddress({ ...addressComponents, country: e.target.value })} />
        </label>
        <br />
        <button type="button" onClick={useCurrentLocation}>
          Use current location
        </button>
      </form>
    </div>
  );
};

export default AddressForm;
```

⚠️ Don't forget to add your Google Maps API key to this file before continuing.

### 3. Use the AddressForm Component

Now, you can use the `AddressForm` component in your `src/App.js`:

```jsx
// src/App.js
import AddressForm from "./AddressForm";

function App() {
  return (
    <div className="App">
      <AddressForm />
    </div>
  );
}

export default App;
```

Run your app:

```bash
npm start
```

Visit [http://localhost:3000](http://localhost:3000) in your browser, you should see the Address Form with autocomplete.

## Option B - Using ExoAPI

ExoAPI offers an affordable and easy-to-use alternative to Google Maps API (and it's 96% cheaper! 🤑):

### 1. Sign Up for ExoAPI

Visit [ExoAPI](https://exoapi.dev?ref=devto) and sign up for an account to obtain your API key.

### 2. Update the AddressForm Component

In `AddressForm.js`, replace the `useCurrentLocation` function to use ExoAPI:

```jsx
const useCurrentLocation = async () => {
  if (!navigator.geolocation) {
    console.error("Geolocation is not supported by this browser.");
    return;
  }

  try {
    // 1. Request current position from device
    const position = await new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject));
    const { latitude, longitude } = position.coords;

    // 2. Query the reverse geocoding API
    const address = await (
      await fetch("https://api.exoapi.dev/reverse-geocoding", {
        method: "POST",
        headers: {
          Authorization: "Bearer <your-api-key>",
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ lat: latitude, lon: longitude, locale: "en-GB" }),
      })
    ).json();
    console.log("[useCurrentLocation]", { latitude, longitude, address });

    // 3. Autocomplete form
    setAddress({
      street: [address.street, address.houseNumber].filter((x) => x).join(" "),
      city: address.city || "",
      postcode: address.postalCode || "",
      country: address.country || "",
    });
  } catch (error) {
    console.error("Error:", error);
  }
};
```

⚠️ Don't forget to replace `<your-api-key>` with the API key you obtained from ExoAPI.

### 3. Remove Google Maps dependencies

ExoAPI works without any external dependencies, so you can uninstall the Google Maps module:

```bash
npm uninstall react-geocode
```

And remove the following lines:

```jsx
// These lines are not needed anymore when using ExoAPI
import { setDefaults, fromLatLng } from "react-geocode";

setDefaults({
  key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
});
```

## Conclusion

Congratulations! 🎉 You now have a flexible React Address Form component that can work with either the Google Maps API or ExoAPI for reverse geocoding. Choose the option that best fits your project requirements and preferences.