Learn fetching address details using Google addresses API (Zipcode or postcode, City, State, Country, Full address) using React js and Formik & Material UI fields with autocomplete suggestions

Auto complete Google addresses API (Reactjs, Material UI, Formik)

Written By: Mohamed Atef

Published: 2021-07-03 | Comments: 2 | Category: React js

Google addresses API with React js 

What is Google Maps JavaScript API?

Google Maps or addresses API is a service from Google Could Platform Which allow you to get the key and start searching or fetching data and details about places or addresses using google, and I am going to show you how to use this service with React js, Formik, Material UI

Setup Google addresses API with React js

In the beginning, I searched and tried a lot of Packages but for sorry around 90% of the packages are not supporting Material UI and it's not gonna be simple to use it with Formik so I created a custom way to figure it out, it's really simple, let's start setting up the google places API all you need to do is just getting your project key, From Google Maps Click on enable  

Then from Credentials > Create Credentials and choose API, 

When you finish this step google will ask you to copy your API key copy it and do the next step 

Start working with React js

You need to load the library of google places into your website, we gonna do this from the App folder > public > index.html then add this script at the end of the head tag

<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE&language=en&libraries=places"></script>

Now you need to make sure that you installed Formik & Material UI & Material Lab 

Install them in case you don't have them

npm install @material-ui/core

Install Formik

 npm install formik --save

Material UI Lab (to import the Autocomplete input)  

npm install @material-ui/lab

Now you need to set up two components include Google functions to make it easy to use them in the parent component 

First file: GoogleAutoCompelete.js

const GoogleAutoCompelete = async text =>
    new Promise((resolve, reject) => {
        if (!text) {
            return reject("Need valid text input")
        }
        
        // for use in things like GatsbyJS where the html is generated first
        if (typeof window === "undefined") {
            return reject("Need valid window object")
        }
        
    try {
        new window.google.maps.places.AutocompleteService().getQueryPredictions(
            { input: text, componentRestrictions: { country: "us" }, fields: ["address_components", "geometry"], },
            resolve
            )
        } catch (e) {
            reject(e)
        }
    })
export default GoogleAutoCompelete;

Second file: GetPostalCode.js

const GetPostalCode = async placeId =>
  new Promise((resolve, reject) => {
    if (!placeId) reject("placeId not provided")

    try {
      new window.google.maps.places.PlacesService(
        document.createElement("div")
      ).getDetails(
        {
          placeId,
          fields: ["address_components"],
        },
        details => {
          return resolve(details)
        }
      )
    } catch (e) {
      reject(e)
    }
  })
export default GetPostalCode;

Then you have to include them in the main component 

import GoogleAutoCompelete from './Google/GoogleAutoCompelete';
import GetPostalCode from './Google/GetPostalCode';

Now create the form with the functions to use the components 

    <Formik
      enableReinitialize={true}
      initialValues={{
        companyAddress: '',
        zipCode: '',
        city: '',
        stateOfAddress: '',
        submit: null
      }}
      validationSchema={Yup.object().shape({
        companyAddress: Yup.string().max(255).required('Company address is required'),
        zipCode: Yup.string().max(255),
        city: Yup.string().max(255),
        stateOfAddress: Yup.string().max(255),
      })}
      onSubmit={async (values, {
        setErrors,
        setStatus,
        setSubmitting,
      }) => {
        try {
          console.log(values);
        } catch (err) {
          console.error(err);
          setStatus({ success: false });
          setErrors({ submit: err.message });
          setSubmitting(false);
        }
      }}
    >
      {({
        errors,
        handleBlur,
        handleChange,
        handleSubmit,
        isSubmitting,
        touched,
        values,
        setFieldValue
      }) => (
        <form
          onSubmit={handleSubmit}
          {...rest}
        >
          <Grid container> 
            <Grid item md={6} xs={12}> 
              <Box mt={3} px={6}>
                <Grid container spacing={3}>
                  <Grid item xs={12}>
                    <Autocomplete
                      options={addresses.map((option) => option.description)}
                      // closeIcon= { () => { return; } }
                      onInputChange={(event, value) => { changeAddress(value, setFieldValue); }}
                      autoComplete={false}
                      renderInput={(params) => (
                          <TextField {...params} label="Company Address" name="companyAddress" value={values.companyAddress} onChange={(value) => { handleChangeAddress(value); }} variant="outlined" />
                      )} 
                    />
                  </Grid>
                  <Grid item xs={12}>
                    <TextField
                      error={Boolean(touched.zipCode && errors.zipCode)}
                      fullWidth
                      helperText={touched.zipCode && errors.zipCode}
                      label="Zip Code"
                      name="zipCode"
                      onBlur={handleBlur}
                      onChange={handleChange}
                      value={values.zipCode}
                      variant="outlined"
                    />
                  </Grid>
                  <Grid item xs={12}>
                    <TextField
                      error={Boolean(touched.city && errors.city)}
                      fullWidth
                      helperText={touched.city && errors.city}
                      label="City"
                      name="city"
                      onBlur={handleBlur}
                      onChange={handleChange}
                      value={values.city}
                      variant="outlined"
                    />
                  </Grid>
                  <Grid item xs={12}>
                    <TextField
                      error={Boolean(touched.stateOfAddress && errors.stateOfAddress)}
                      fullWidth
                      helperText={touched.stateOfAddress && errors.stateOfAddress}
                      label="State (Administrative Area)"
                      name="stateOfAddress"
                      onBlur={handleBlur}
                      onChange={handleChange}
                      value={values.stateOfAddress}
                      variant="outlined"
                    />
                  </Grid>
                </Grid>
                {Boolean(touched.tags && errors.tags) && (
                  <Box mt={2}>
                    <FormHelperText error>
                      {errors.tags}
                    </FormHelperText>
                  </Box>
                )}
                {Boolean(touched.startDate && errors.startDate) && (
                  <Box mt={2}>
                    <FormHelperText error>
                      {errors.startDate}
                    </FormHelperText>
                  </Box>
                )}
                {Boolean(touched.endDate && errors.endDate) && (
                  <Box mt={2}>
                    <FormHelperText error>
                      {errors.endDate}
                    </FormHelperText>
                  </Box>
                )}
              </Box>
              <Box
                mt={12}
                display="flex"
              >
                <Box flexGrow={1} />
                <Button
                  color="secondary"
                  disabled                  type="submit"
                  variant="contained"
                  size="large"
                >
                  Submit
                </Button>
              </Box>
            </Grid>
          </Grid>
        </form>
      )}
    </Formik>

All we need to use the Google components 

  const [addresses, setAddresses] = useState([]);
  
  const handleChangeAddress = async (searchValue) => {
    const results = await GoogleAutoCompelete(searchValue.target.value);
    if (results) {
      setAddresses(results);
    }
  }

  const changeAddress = async (value, setFieldValue) => {
        let result = null;
        for(let x = 0; x < addresses.length; x++){
          if(value === addresses[x].description){
            result = await GetPostalCode(addresses[x].place_id);
            // Get Zip code
          }
        }
        if(!result){ return ; }
        setFieldValue('companyAddress', value);
        let postcode = null;
        for(let i = 0; i < result.address_components.length; i++){
          if(result.address_components[i].types[0] === 'postal_code'){
              postcode = result.address_components[i].long_name;
          }
        }
        setFieldValue('zipCode', postcode);
        // Get city
        let city = null;
        for(let i = 0; i < result.address_components.length; i++){
          if(result.address_components[i].types[0] === 'locality'){
              city = result.address_components[i].long_name;
          }
        }
        if(!city){
          for(let i = 0; i < result.address_components.length; i++){
            if(result.address_components[i].types[0] === 'administrative_area_level_2'){
                city = result.address_components[i].long_name;
            }
          }
        }
        setFieldValue('city', city);

        // Get State
        let state = null;
        for(let i = 0; i < result.address_components.length; i++){
          if(result.address_components[i].types[0] === 'administrative_area_level_1'){
            state = result.address_components[i].long_name;
          }
        }
        if(!state){
          for(let i = 0; i < result.address_components.length; i++){
            if(result.address_components[i].types[0] === 'administrative_area_level_2'){
              state = result.address_components[i].long_name;
            }
          }
        }
        setFieldValue('stateOfAddress', state);
  }

That's all and here is the content of the main component (AutoFillForm.js)

import React, { useState, useEffect } from 'react';
import * as Yup from 'yup';
import { Formik } from 'formik';
import {
  Box,
  Button,
  FormHelperText,
  Grid,
  TextField,
} from '@material-ui/core';
import Autocomplete from '@material-ui/lab/Autocomplete';
import GoogleAutoCompelete from './Google/GoogleAutoCompelete';
import GetPostalCode from './Google/GetPostalCode';


const AutoFillForm = ({
  className,
  setData,
  data,
  projectId,
  ...rest
}) => {
  const [addresses, setAddresses] = useState([]);
  
  const handleChangeAddress = async (searchValue) => {
    if(!searchValue.target.value){
      return null;
    }
    const results = await GoogleAutoCompelete(searchValue.target.value);
    if (results) {
      setAddresses(results);
    }
  }

  const changeAddress = async (value, setFieldValue) => {
        let result = null;
        for(let x = 0; x < addresses.length; x++){
          if(value === addresses[x].description){
            result = await GetPostalCode(addresses[x].place_id);
            // Get Zip code
          }
        }
        if(!result){ return ; }
        setFieldValue('companyAddress', value);
        let postcode = null;
        for(let i = 0; i < result.address_components.length; i++){
          if(result.address_components[i].types[0] === 'postal_code'){
              postcode = result.address_components[i].long_name;
          }
        }
        setFieldValue('zipCode', postcode);
        // Get city
        let city = null;
        for(let i = 0; i < result.address_components.length; i++){
          if(result.address_components[i].types[0] === 'locality'){
              city = result.address_components[i].long_name;
          }
        }
        if(!city){
          for(let i = 0; i < result.address_components.length; i++){
            if(result.address_components[i].types[0] === 'administrative_area_level_2'){
                city = result.address_components[i].long_name;
            }
          }
        }
        setFieldValue('city', city);

        // Get State
        let state = null;
        for(let i = 0; i < result.address_components.length; i++){
          if(result.address_components[i].types[0] === 'administrative_area_level_1'){
            state = result.address_components[i].long_name;
          }
        }
        if(!state){
          for(let i = 0; i < result.address_components.length; i++){
            if(result.address_components[i].types[0] === 'administrative_area_level_2'){
              state = result.address_components[i].long_name;
            }
          }
        }
        setFieldValue('stateOfAddress', state);
  }

	//We can use this function to disable the browser auto complete from the fields because it looks really annoying
  useEffect(() => {
    window.document.querySelector('input[name="companyAddress"]').setAttribute('autocomplete', 'disable');
    window.document.querySelector('input[name="companyAddress"]').setAttribute('aria-autocomplete', 'off');
    window.document.querySelector('input[name="zipCode"]').setAttribute('autocomplete', 'disable');
    window.document.querySelector('input[name="zipCode"]').setAttribute('aria-autocomplete', 'off');
    window.document.querySelector('input[name="city"]').setAttribute('autocomplete', 'disable');
    window.document.querySelector('input[name="city"]').setAttribute('aria-autocomplete', 'off');
    window.document.querySelector('input[name="stateOfAddress"]').setAttribute('autocomplete', 'disable');
    window.document.querySelector('input[name="stateOfAddress"]').setAttribute('aria-autocomplete', 'off');
  }, []);
  return (
    <Formik
      enableReinitialize={true}
      initialValues={{
        companyAddress: '',
        zipCode: '',
        city: '',
        stateOfAddress: '',
        submit: null
      }}
      validationSchema={Yup.object().shape({
        companyAddress: Yup.string().max(255).required('Company address is required'),
        zipCode: Yup.string().max(255),
        city: Yup.string().max(255),
        stateOfAddress: Yup.string().max(255),
      })}
      onSubmit={async (values, {
        setErrors,
        setStatus,
        setSubmitting,
      }) => {
        try {
          console.log(values);
        } catch (err) {
          console.error(err);
          setStatus({ success: false });
          setErrors({ submit: err.message });
          setSubmitting(false);
        }
      }}
    >
      {({
        errors,
        handleBlur,
        handleChange,
        handleSubmit,
        isSubmitting,
        touched,
        values,
        setFieldValue
      }) => (
        <form
          onSubmit={handleSubmit}
          {...rest}
        >
          <Grid container> 
            <Grid item md={6} xs={12}> 
              <Box mt={3} px={6}>
                <Grid container spacing={3}>
                  <Grid item xs={12}>
                    <Autocomplete
                      options={addresses.map((option) => option.description)}
                      // closeIcon= { () => { return; } }
                      onInputChange={(event, value) => { changeAddress(value, setFieldValue); }}
                      autoComplete={false}
                      renderInput={(params) => (
                          <TextField {...params} label="Company Address" name="companyAddress" value={values.companyAddress} onChange={(value) => { handleChangeAddress(value); }} variant="outlined" />
                      )} 
                    />
                  </Grid>
                  <Grid item xs={12}>
                    <TextField
                      error={Boolean(touched.zipCode && errors.zipCode)}
                      fullWidth
                      helperText={touched.zipCode && errors.zipCode}
                      label="Zip Code"
                      name="zipCode"
                      onBlur={handleBlur}
                      onChange={handleChange}
                      value={values.zipCode}
                      variant="outlined"
                    />
                  </Grid>
                  <Grid item xs={12}>
                    <TextField
                      error={Boolean(touched.city && errors.city)}
                      fullWidth
                      helperText={touched.city && errors.city}
                      label="City"
                      name="city"
                      onBlur={handleBlur}
                      onChange={handleChange}
                      value={values.city}
                      variant="outlined"
                    />
                  </Grid>
                  <Grid item xs={12}>
                    <TextField
                      error={Boolean(touched.stateOfAddress && errors.stateOfAddress)}
                      fullWidth
                      helperText={touched.stateOfAddress && errors.stateOfAddress}
                      label="State (Administrative Area)"
                      name="stateOfAddress"
                      onBlur={handleBlur}
                      onChange={handleChange}
                      value={values.stateOfAddress}
                      variant="outlined"
                    />
                  </Grid>
                </Grid>
                {Boolean(touched.tags && errors.tags) && (
                  <Box mt={2}>
                    <FormHelperText error>
                      {errors.tags}
                    </FormHelperText>
                  </Box>
                )}
                {Boolean(touched.startDate && errors.startDate) && (
                  <Box mt={2}>
                    <FormHelperText error>
                      {errors.startDate}
                    </FormHelperText>
                  </Box>
                )}
                {Boolean(touched.endDate && errors.endDate) && (
                  <Box mt={2}>
                    <FormHelperText error>
                      {errors.endDate}
                    </FormHelperText>
                  </Box>
                )}
              </Box>
              <Box
                mt={12}
                display="flex"
              >
                <Box flexGrow={1} />
                <Button
                  color="secondary"
                  disabled                  type="submit"
                  variant="contained"
                  size="large"
                >
                  Submit
                </Button>
              </Box>
            </Grid>
          </Grid>
        </form>
      )}
    </Formik>
  );
};

export default AutoFillForm;

If you have any questions let me know in the comments below 
Thanks

Comments

Clement Gitonga Date : 2022-03-19

I got an error “Expected to return a value at the end of arrow function”  on this line in GoogleAutoComplete

 new Promise((resolve, reject) => {

Clement Gitonga Date : 2022-03-23

I figured out the solution to the error above. Thank you for this blog post I was able to solve a major problem. in addition I used the same concept to get Distance between two points.

Leave a comment

Join us

Join our community and get the chance to solve your code issues & share your opinion with us

Sign up Now

Related posts

Show CSV file in web page using Reactjs
Publish date: 2021-10-20 | Comments: 0

Tag: React js

Deploy Reactjs using Azure
Publish date: 2021-07-31 | Comments: 0

Tag: React js

Display PDF in Reactjs from Authenticated API
Publish date: 2022-02-14 | Comments: 0

Tag: React js

CSS media query (breakpoints) in Material UI
Publish date: 2021-10-21 | Comments: 0

Tag: React js

Hide errors in live website Reactjs
Publish date: 2022-03-31 | Comments: 0

Tag: React js