Advanced - Search This Area | Yext Hitchhikers Platform

What You’ll Learn

In this section, you will:

  • Display a Search This Area button on the map when the map is dragged
  • Trigger a search for locations on an area when the button is clicked

Overview

Many locators, such as Yelp and Google Maps, enable users to initiate a new search after the visible map boundaries have changed in response to when the user drags on the map. In this section, you will update your locator to mimic this behavior, by using the MapboxMap onDrag prop to display a “Search This Area” button when the center of the map changes.


1. Add Imports and State Values

Add imports for the LngLat and LngLatBounds types from Mapbox GL and the OnDragHandler type from @yext/search-ui-react:

// src/components/StoreLocator.tsx

import * as React from "react";
import {
  MapboxMap,
  FilterSearch,
  OnSelectParams,
  VerticalResults,
  getUserLocation,
  OnDragHandler,                                             // New
} from "@yext/search-ui-react";
import { useEffect, useState } from "react";
import { BiLoaderAlt } from "react-icons/bi";
import {
  Matcher,
  SelectableStaticFilter,
  useSearchActions,
  useSearchState,
} from "@yext/search-headless-react";
// Mapbox CSS bundle
import "mapbox-gl/dist/mapbox-gl.css";
import LocationCard from "./LocationCard";
import MapPin from "./MapPin";
import { LngLat, LngLatBounds } from "mapbox-gl";             // New
// component code...

Then, in the component, grab the resultCount from the search state and create some new state variables using the useState hook:

// src/components/StoreLocator.tsx

// imports...

const StoreLocator = (): JSX.Element => {
  const resultCount = useSearchState(
    (state) => state.vertical.resultsCount || 0
  );
  const [showSearchAreaButton, setShowSearchAreaButton] = useState(false);
  const [mapCenter, setMapCenter] = useState<LngLat | undefined>();
  const [mapBounds, setMapBounds] = useState<LngLatBounds | undefined>();

  // other code from previous units...
}

You will be using these new variables in future steps.

2. Add the handleDrag Function

You need an onDrag handler that MapboxMap will call when the center of the map changes. Add the handleDrag function to your component:

// src/components/StoreLocator.tsx

// imports...

const StoreLocator = (): JSX.Element => {

  // other code from previous units/steps

  const handleDrag: OnDragHandler = (center: LngLat, bounds: LngLatBounds) => {
    setMapCenter(center);
    setMapBounds(bounds);
    setShowSearchAreaButton(true);
  };

  // other code from previous units/steps
}

handleDrag will set the current map center and bounds and toggle the showSearchAreaButton variable to true.

3. Add the handleSearchAreaClick Function

You’re eventually going to add a “Search This Area” button that will appear over top of the map if the center of the map changes. Before you add the button, add the handleSearchAreaClick handler function to the component:

// src/components/StoreLocator.tsx

// imports...

const StoreLocator = (): JSX.Element => {
	
  // other code from previous units/steps

  const handleSearchAreaClick = () => {
    if (mapCenter && mapBounds) {
      const locationFilter: SelectableStaticFilter = {
        selected: true,
        displayName: "Current map area",
        filter: {
          kind: "fieldValue",
          fieldId: "builtin.location",
          value: {
            lat: mapCenter.lat,
            lng: mapCenter.lng,
            radius: mapBounds.getNorthEast().distanceTo(mapCenter),
          },
          matcher: Matcher.Near,
        },
      };
      searchActions.setStaticFilters([locationFilter]);
      searchActions.executeVerticalQuery();
      setShowSearchAreaButton(false);
    }
  };

  // other code from previous units/steps
};

handleSearchArea sets a filter in the search state based on the distance between the center and northeast corner of the map before running a new vertical query. Finally, it toggles showSearchAreaButton to false.

4. Render the Search This Area Button

The last step is to conditionally render to the “Search This Area” button on top of the map if the center of the map changed. The new button will use handleSearchAreaClick as its onClick handler.

In addition, sometimes a given area won’t have any locations. To handle this, you will display “No results found for this area” when there are no results in the given map area.

Swap out the template code with the following to add the button:

// src/components/StoreLocator.tsx

// imports...

const StoreLocator = (): JSX.Element => {

// other code from previous units/steps

  return (
    <>
      <div className="relative flex h-[calc(100vh-210px)] border">
        {initialSearchState !== "complete" && (
          <div className="absolute z-20 flex h-full w-full items-center justify-center bg-white opacity-70">
            <BiLoaderAlt className="animate-spin " size={64} />
          </div>
        )}
        <div className="w-1/3 flex flex-col">
          <FilterSearch
            onSelect={handleFilterSelect}
            placeholder="Find Locations Near You"
            searchFields={[
              {
                entityType: "location",
                fieldApiName: "builtin.location",
              },
            ]}
          />
          {/* new code starts here... */}
          {resultCount > 0 && <VerticalResults CardComponent={LocationCard} />}
          {resultCount === 0 && initialSearchState === "complete" && (
            <div className="flex items-center justify-center">
              <p className="pt-4 text-2xl">No results found for this area</p>
            </div>
          )}
        </div>
        <div className="relative w-2/3">
          <MapboxMap
            mapboxAccessToken={YEXT_PUBLIC_MAPBOX_API_KEY || ""}
            PinComponent={MapPin}
            onDrag={handleDrag}
          />
          {showSearchAreaButton && (
            <div className="absolute bottom-10 left-0 right-0 flex justify-center">
              <button
                onClick={handleSearchAreaClick}
                className="rounded-2xl border bg-white py-2 px-4 shadow-xl"
              >
                <p>Search This Area</p>
              </button>
            </div>
          )}
          {/* ...and ends here */}
        </div>
      </div>
    </>
  );
};

export default StoreLocator;

If your site isn’t already running, start it back up with npm run dev and go the locator page. After the map loads for the first time, drag your map to the Miami, Florida area. Click on the “Search This Area” button to see your new functionality in action.

light bulb
Note
You could also have the map automatically search onDrag rather than triggering the search with a button click. You can see an example of how to do this on the MapboxMap reference page .
unit Quiz
+20 points
Daily Quiz Streak Daily Quiz Streak: 0
Quiz Accuracy Streak Quiz Accuracy Streak: 0
    Error Success Question 1 of 1

    Which function updates the map center when the map is moved?

    Soon you'll be your brand's hero! 🎓

    You've already completed this quiz, so you can't earn more points.You completed this quiz in 1 attempt and earned 0 points! Feel free to review your answers and move on when you're ready.
1st attempt
0 incorrect
Feedback