How-to: Use AutoSuggest in React

Let’s learn how we can use the React Autosuggest library that will show a dynamic list of countries that is being loaded from a JSON endpoint. We will be using this within a Formik form. Personally, I love using Formik because of its easy to understand and implement.

If you don’t know Formik yet, I suggest you visit https://formik.org/ . They have great documentation and a tutorial that you can follow to quickly set up Formik. I also use Yup for my validation. Just click on the click on how to install it.

npm install formik --save
yarn add formik


yarn add yup
npm install yup

import * as yup from 'yup'; // for everything
import { string, object } from 'yup'; // for only what you need

npm install @types/yup // if you are using Typescript 

After you have set up your Formik form or Vanilla form, let’s install React autosuggest.

$: yarn add react-autosuggest

First thing we need to do is go to our Formik form and use the autoSuggest on our form. I have already created a simple Formikform in my previous blog, and we will just reuse it to add the React autosuggest.

import React from "react";
import { Formik } from "formik";
import * as Yup from "yup";
import Error from "./forms-schema/formik-error-message";

const validationSchema = Yup.object().shape({
  name: Yup.string()
    .min(2, "Must have at least 2 characters")
    .max(225, "Must be less than 255 characters")
    .required("Must enter a name"),
  email: Yup.string()
    .email("Must be a valid email address")
    .max(225, "Must be less than 255 characters")
    .required("Must enter an email"),
});

export default function FormikForm() {
  return (
    <Formik
      initialValues={{ name: "", email: " " }}
      validationSchema={validationSchema}
      onSubmit={(values, { setSubmitting, resetForm }) => {
        setSubmitting(true);
      
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          resetForm();
          setSubmitting(false);
        }, 500);
      }}
    >
      {/* render props*/}
      {({
        values,
        errors,
        touched,
        handleChange,
        handleBlur,
        handleSubmit,
        isSubmitting,
      }) => (
        <form onSubmit={handleSubmit}>
          {/* to show values in the UI for debugging purposes */}
          {JSON.stringify(values, null, 2)}

          <div className="input-row">
            <label htmlFor="name"> Name</label>
            <input
              type="text"
              id="name"
              name="name"
              placeholder="Full Name"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.name}
              className={touched.name && errors.name ? "has-error" : null}
            />
            <Error touched={touched.name} message={errors.name} />
          </div>

          <div className="input-row">
            <label htmlFor="email"> Email </label>
            <input
              type="email"
              id="email"
              name="email"
              placeholder="Email"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.email}
              className={touched.email && errors.email ? "has-error" : null}
            />

            <Error touched={touched.email} message={errors.email} />
          </div>

									{/* Place Autosuggesst here    */}
	
          <div className={"input-row"}>
            <button type="submit" disabled={isSubmitting}>
              Submit{" "}
            </button>
          </div>
        </form>
      )}
    </Formik>
  );
}

We can put the autoSuggest right after our email field. The autosuggest basically allows the users to search through different countries and by typing its going to pop up different country names.

         <label htmlFor="country">Country</label>
              <Autosuggest inputProps={{   
        
              }}/>      

Autosuggest controls the actual inputs that’s being shown to the user so let’s create some standard input props.

         <label htmlFor="country">Country</label>
              <Autosuggest inputProps={{ 
                placeholder: "Type Country",
                autoComplete: "",
                name: "country",
                id: "country"
                      
              }} />

We might be getting some errors after creating our Autosuggest because we’ve got of things we need to add before it works.

The next thing we need to do is to declare some values or states that we need keep track of.

export default function FormikForm() {

  const [country, setCountry] = useState("");
  const [suggestions, setSuggestions] = useState([]);

  return (...

Then let’s pass the setSuggestions array to our Autosuggest. We also need to pass our recently set value, country, and onChange event to the inputProps.

<label htmlFor="country">Country</label>
              <Autosuggest inputProps={{ 
                placeholder: "Type Country",
                autoComplete: "",
                name: "country",
                id: "country",
								value: country, 
								onChange: (_event, { newValue }) => {
                  setCountry(newValue); 
                }
                      
              }}
                suggestions={suggestions}
                
                />

Check you app if the country field appears. At this point though, it is not yet working because we still need to add a lot of props into our inputProps.

Another prop we need to implement is the onSuggestionsFetchRequested, which will be called based on the user’s inputs and updates the suggestion state.

								<Autosuggest inputProps={{ 
                placeholder: "Type Country",
                autoComplete: "",
                name: "country",
                id: "country", 
                value: country,
                onChange: (_event, { newValue }) => {
                  setCountry(newValue); 
                }    
              }}
                suggestions={suggestions}
                onSuggestionsFetchRequested={async ({ value }) => {
                  if (!value) {
                    setSuggestions([]);
                    return;
                  }
                }}
								onSuggestionsClearRequested={() => {
                  setSuggestions([]); 
                }}

The onSuggestionsClearRequested sets the suggestions back into an empty array after the user has sent a request.

The next function we need to define is the getSuggestionValue.

					onSuggestionsClearRequested={() => {
                setSuggestions([]);
              }}
              getSuggestionValue={(suggestion) => suggestion.name}
              renderSuggestion={(suggestion) => <div> {suggestion.name} </div>}
            />
          </div>

Hold on. At this point, we need to implement Axios on our onSuggestionsFetchRequested, which we would also need to wrap in a try catch to handle any axios call fail.

onSuggestionsFetchRequested={async ({ value }) => {
                if (!value) {
                  setSuggestions([]);
                  return;
                }
                try {

								{ /* Axios here   */}

                } catch (e) {
                  setSuggestions([]);
                }
              }}

The result or data we will get back, we need to map it to our setSuggestions.

...
try {
                    { /* Axios here   */}
                    const result = await.axios.get('https://restcountries.eu/rest/v2name');

                    setSuggestions(
                        response.data.map(row => ({
                                name: row.name
                            }
                        ))
                    );
                } catch (e) {
                  setSuggestions([]);
                }
              }}
onSuggestionSelected={(event, { suggestion, method }) => {
                if (method === "enter") {
                  event.preventDefault();
                }
                setCountry(suggestion.name);
                setFieldValue("country", suggestion.name);
              }}
              inputProps={{
                placeholder: "Search for your country",
                autoComplete: "abcd",
                value: country,
                name: "country",
                onChange: (_event, { newValue }) => {
                  setCountry(newValue);
                },
                className:
                  touched.country && errors.country ? "has-error" : null
              }}
            />
            <Error touched={touched.country} message={errors.country} />
          </div>

          {showPostalCode(values.country) ? (
            <div className="input-row">
              <label>{postalCodeLabel(values.country)}</label>
              <input
                type="text"
                name="postalCode"
                onChange={handleChange}
                onBlur={handleBlur}
                value={values.postalCode}
                className={
                  touched.postalCode && errors.postalCode ? "has-error" : null
                }
              />
              <Error touched={touched.postalCode} message={errors.postalCode} />
            </div>
          ) : null}