google-maps-api-3shadcnui

Google Maps Autocomplete fails to suggest locations when under Shadcn Accordion


I'm using the Google Maps JavaScript API for my website along with the Place Autocomplete service. The problem is that the Autocomplete component won't suggest any locations when it's placed inside of a Shadcn Accordion. Here's a minimum reproducible example to show this problem:

import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../components/ui/accordion"
import { Input } from "../components/ui/input";
import { useEffect, useRef } from "react";

export default function AccountSettings() {
  const autoCompleteRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (!autoCompleteRef.current) return;
    new google.maps.places.Autocomplete(autoCompleteRef.current);
  }, []);

  return (
    <div>
      <Accordion type="single" collapsible>
        <AccordionItem value="item-1">
          <AccordionTrigger>
            <span>Location Accordion</span>
          </AccordionTrigger>
          <AccordionContent>
            <Input ref={autoCompleteRef} placeholder="Choose a location" />
          </AccordionContent>
        </AccordionItem>
      </Accordion>
    </div>
  )
}

Using the arrow keys and pressing Enter does nothing, indicating that the autocomplete suggestions do not exist at all. If I remove the Accordion and place the Input component on the main page, Autocomplete suggestions will show up correctly.

There's no errors in the JavaScript console to suggest that anything is wrong. One thing I noticed is if I open the Accordion, make changes to my code, and then go back to the dev instance, the Autocomplete component will suggest locations which is the correct behavior. I tried keeping track of the open state of the accordion, and then conditionally rendering the Autocomplete input using isAccordionOpen &&, but that didn't do anything.

How can I get the Google Autocomplete component to suggest locations when it's inside a Shadcn Accordion?


Solution

  • Found the answer. It turns out you need to use forceMount to render the Autocomplete Input independent of the open state of the accordion. This makes sure the Accordion content is rendered into the DOM regardless of the open state. Then, use CSS to hide the Input when the accordion is not open.

    Here's the provided example with the updated code:

    export default function AccountSettings() {
      // Keep track of open state
      const [isAccordionExpanded, setIsAccordionExpanded] = useState("");
      const autoCompleteRef = useRef<HTMLInputElement>(null);
    
      useEffect(() => {
        if (!autoCompleteRef.current) return;
        new google.maps.places.Autocomplete(autoCompleteRef.current);
      }, []);
    
      return (
        <div>
          <Accordion type="single"
              collapsible
              onValueChange={setIsAccordionExpanded}
              value={isAccordionExpanded}>
            <AccordionItem value="item-1">
              <AccordionTrigger>
                <span>Location Accordion</span>
              </AccordionTrigger>
              {/* Use forceMount here and then hide the content when the accordion is closed */}
              <AccordionContent forceMount className={`${isAccordionExpanded ? 'block' : 'hidden'}`}>
                <Input ref={autoCompleteRef} placeholder="Choose a location" />
              </AccordionContent>
            </AccordionItem>
          </Accordion>
        </div>
      );
    }
    

    I was using tailwindCSS for that example, but you can also use regular CSS for the same hiding effect using display: none and display: block