Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError ?(maps-api-v3/api/js/58/8-beta/intl/nl_ALL/map) #863

Open
Oigen43 opened this issue Oct 14, 2024 · 3 comments
Open

TypeError ?(maps-api-v3/api/js/58/8-beta/intl/nl_ALL/map) #863

Oigen43 opened this issue Oct 14, 2024 · 3 comments
Labels
needs more info This issue needs more information from the customer to proceed.

Comments

@Oigen43
Copy link

Oigen43 commented Oct 14, 2024

Issue Report

Summary

We are encountering an intermittent TypeError with the Map component in our application. The error appears to be related to the Google Maps API, specifically with the instantiation of an object in the Map library.

Error Details

  • Error Type: TypeError
  • Error Message: undefined is not an object (evaluating 'new Ja.OC')
  • Error Path: (maps-api-v3/api/js/58/8-beta/intl/nl_ALL/map)
  • Severity Level: Error

Environment Details

  1. Google Maps API Version: @googlemaps/js-api-loader": "1.16.8
  2. Browser Compatibility: Occurs across all major browsers.
  3. Map Library: @googlemaps/js-api-loader

Steps to Reproduce

This error occurs sporadically across different parts of the application where the Map component is used. There are no specific steps to reliably reproduce it, but it seems to be triggered when loading or interacting with the Map component.

Custom Map Component (React)

import { useEffect, useRef, useState, FC, ReactNode, useCallback } from 'react';
import {
  Space,
  useMantineTheme,
  Box,
  ActionIcon,
  rem,
  Sx,
} from '@mantine/core';
import { Loader } from '@googlemaps/js-api-loader';

import { config } from '../../config';
import Input from '../Input';
import { IconXClose } from '../../icons';
import styles from './styles';

const defaultZoom = 15;
const defaultCenter = {
  lat: 52.3727,
  lng: 4.8931,
};

interface CustomInputContainerProps {
  children: ReactNode;
}

const AUTOCOMPLETE_OPTIONS = {
  fields: ['formatted_address', 'geometry', 'name', 'place_id'],
  strictBounds: false,
  types: ['geocode'],
};

const ARROW_DOWN_KEY = {
  bubbles: true,
  cancelable: true,
  key: 'Down',
  code: 'Down',
  keyCode: 40,
  which: 40,
};

const ENTER_KEY = {
  bubbles: true,
  cancelable: true,
  key: 'Enter',
  code: 'Enter',
  keyCode: 13,
  which: 13,
  triggered: true,
};

const Map = ({
  currentLocation,
  currentCoordinates,
  updateInput,
  inputPlaceholder,
  error,
  disableInput,
  inputTooltipPosition,
  inputTooltipTitle,
  inputTooltipDescription,
  CustomInputContainer,
  center = defaultCenter,
  zoom = defaultZoom,
  isHidden,
  readOnly,
  mapSx = {},
  noPaddings = false,
  ...props
}: {
  currentLocation?: string | null;
  currentCoordinates?: { lat: number; lng: number } | null;
  updateInput?: (location: string, placeId?: string) => void;
  inputPlaceholder?: string;
  error?: string;
  disableInput?: boolean;
  inputTooltipPosition?: string;
  inputTooltipTitle?: ReactNode;
  inputTooltipDescription?: string | null;
  center?: google.maps.LatLngLiteral;
  zoom?: number;
  CustomInputContainer?: FC<CustomInputContainerProps>;
  isHidden?: boolean;
  readOnly?: boolean;
  sx?: Sx;
  mapSx?: Sx;
  noPaddings?: boolean;
}) => {
  const theme = useMantineTheme();
  const ref = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const autocomplete = useRef<google.maps.places.Autocomplete | null>(null);
  const marker = useRef<google.maps.marker.AdvancedMarkerElement>();
  const circle = useRef<google.maps.Circle>();
  const mapRef = useRef<google.maps.Map>();
  const geocoder = useRef<google.maps.Geocoder>();

  const markerSvg = document.createElement('div');
  markerSvg.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="38" height="38" fill="none">
      <path fill=${
        theme.colors[theme.primaryColor][7]
      } d="M19 34.833c6.333-6.333 12.667-12.004 12.667-19 0-6.995-5.671-12.666-12.667-12.666-6.996 0-12.667 5.67-12.667 12.666S12.667 28.5 19 34.833Z"/>
      <path fill="#FFFFFF" d="M19 20.583a4.75 4.75 0 1 0 0-9.5 4.75 4.75 0 0 0 0 9.5Z"/>
    </svg>`;
  const [address, setAddress] = useState<string>(currentLocation || '');
  const [coordinates, setCoordinates] = useState<
    { lat: number; lng: number } | undefined | null
  >(currentCoordinates);
  const [isScriptLoaded, setIsScriptLoaded] = useState(false);

  const { language } = config.i18n;

  const handleBlur = () => {
    if (!isScriptLoaded) return;

    if (!inputRef.current?.value || inputRef.current?.value.length === 0) {
      if (updateInput) {
        updateInput('');
      }
      setAddress('');
      setCoordinates(null);
      return;
    }

    // to always select the first option from the search
    // because we do not have an exact list of options - simulate the choice
    const arrowDownKeyEvent = new KeyboardEvent('keydown', ARROW_DOWN_KEY);
    google.maps.event.trigger(inputRef.current, 'keydown', arrowDownKeyEvent);
    const enterKeyEvent = new KeyboardEvent('keydown', ENTER_KEY);
    google.maps.event.trigger(inputRef.current, 'keydown', enterKeyEvent);
  };

  const handleLocationChange = useCallback(() => {
    if (
      !isScriptLoaded ||
      !mapRef.current ||
      !google.maps.marker.AdvancedMarkerElement
    )
      return;
    const place = autocomplete?.current?.getPlace();

    if (!place?.geometry || !place?.geometry.location) {
      // User entered the name of a Place that was not suggested and
      // pressed the Enter key, or the Place Details request failed.
      return;
    }

    // If the place has a geometry, then present it on a map.
    if (place?.geometry.viewport) {
      mapRef.current.fitBounds(place?.geometry.viewport);
    } else {
      mapRef.current.setCenter(place?.geometry.location);
      mapRef.current.setZoom(zoom);
    }

    if (marker.current) {
      marker.current.map = null; // Remove marker from the map
    }

    if (marker.current) {
      marker.current = new google.maps.marker.AdvancedMarkerElement({
        position: isHidden
          ? null
          : new google.maps.LatLng(
              place.geometry.location.lat(),
              place.geometry.location.lng()
            ),
        map: mapRef.current,
        content: markerSvg,
      });
    }
    circle.current?.setCenter(null);
    if (updateInput) updateInput(place.formatted_address || '', place.place_id);
    setAddress(place.formatted_address || '');
    setCoordinates({
      lng: place.geometry.location.lng(),
      lat: place.geometry.location.lat(),
    });
    if (place.formatted_address && inputRef.current) {
      inputRef.current.value = place.formatted_address;
    }
  }, [isScriptLoaded, updateInput, zoom]);

  useEffect(() => {
    const loader = new Loader({
      apiKey: config.googleMapsApiKey,
      language,
      version: 'beta',
      libraries: ['places', 'marker'],
    });

    loader.load().then(() => {
      if (!google?.maps?.Map || !ref.current) return;

      mapRef.current = new google.maps.Map(ref.current, {
        center,
        zoom,
        styles,
        fullscreenControl: false,
        mapTypeControl: false,
        streetViewControl: false,
        keyboardShortcuts: false,
        mapId: 'ofcorz_map',
      });
      geocoder.current = new google.maps.Geocoder();

      setIsScriptLoaded(true);
    });

    return () => {
      setIsScriptLoaded(false);
      if (!window.google?.maps?.Map) return;

      (Loader as any).instance = null;
      loader.deleteScript();

      delete (window as any).google;

      marker.current = undefined;
      mapRef.current = undefined;
      circle.current = undefined;
    };
  }, [center, language, zoom]);

  useEffect(() => {
    if (!isScriptLoaded || !google.maps?.marker?.AdvancedMarkerElement) return;

    if (!coordinates && currentCoordinates) {
      setCoordinates(currentCoordinates);
    }

    if (currentLocation) {
      if (marker.current) {
        marker.current.map = null;
      }
      marker.current = new google.maps.marker.AdvancedMarkerElement({
        position: isHidden ? null : center,
        map: mapRef.current,
        content: markerSvg,
      });
    }

    circle.current = new google.maps.Circle({
      map: mapRef.current,
      radius: 100,
      center: isHidden ? center : null,
      fillColor: theme.colors[theme.primaryColor][7],
      strokeWeight: 0,
    });
  }, [
    isHidden,
    center,
    zoom,
    currentLocation,
    isScriptLoaded,
    theme.colors,
    theme.primaryColor,
  ]);

  useEffect(() => {
    if (
      currentLocation &&
      isScriptLoaded &&
      geocoder.current &&
      google.maps?.marker?.AdvancedMarkerElement
    ) {
      const location =
        coordinates || currentCoordinates
          ? { location: coordinates || currentCoordinates }
          : { address: currentLocation };

      geocoder.current
        .geocode(location, (results, status) => {
          if (results && results[0] && status === 'OK' && mapRef.current) {
            mapRef.current.fitBounds(results[0]?.geometry.viewport);
            mapRef.current.setCenter(results[0].geometry.location);
            if (marker.current) {
              marker.current.map = null; // Remove marker from the map
              marker.current = new google.maps.marker.AdvancedMarkerElement({
                position: isHidden
                  ? null
                  : new google.maps.LatLng(
                      results[0].geometry.location.lat(),
                      results[0].geometry.location.lng()
                    ),
                map: mapRef.current,
                content: markerSvg,
              });
            }
          }
        })
        .catch((e) => console.log(e));
      if (inputRef && inputRef.current) {
        inputRef.current.value = currentLocation;
      }
    } else if (inputRef && inputRef.current) {
      inputRef.current.value = '';
    }
  }, [currentLocation, isScriptLoaded, language]);

  useEffect(() => {
    if (
      !readOnly &&
      updateInput &&
      isScriptLoaded &&
      google.maps?.places?.Autocomplete
    ) {
      autocomplete.current = new google.maps.places.Autocomplete(
        inputRef.current as HTMLInputElement,
        AUTOCOMPLETE_OPTIONS
      );

      if (mapRef.current) {
        autocomplete.current.bindTo('bounds', mapRef.current);
      }

      autocomplete.current.addListener('place_changed', handleLocationChange);
    }
  }, [isScriptLoaded]);

  const InputContainer = CustomInputContainer || Box;

  return (
    <Box {...props}>
      <Box
        sx={{ borderRadius: rem(8), ...mapSx }}
        ref={ref}
        id="map"
        h={140}
      />
      {updateInput && (
        <>
          {!noPaddings && <Space h="sm" />}
          <InputContainer
            sx={
              noPaddings
                ? { width: '100%', marginTop: rem(10) }
                : { margin: rem(16) }
            }
          >
            <Input
              defaultValue={address}
              ref={inputRef}
              placeholder={inputPlaceholder}
              error={error}
              disabled={disableInput}
              tooltipPosition={inputTooltipPosition}
              tooltipTitle={inputTooltipTitle}
              tooltipDescription={inputTooltipDescription}
              onBlur={handleBlur}
              readOnly={readOnly}
              customRightSection={
                !readOnly && (
                  <ActionIcon
                    onClick={() => {
                      if (inputRef && inputRef.current) {
                        inputRef.current.value = '';
                        inputRef.current.focus();
                        updateInput('');
                        setAddress('');
                        setCoordinates(null);
                      }
                    }}
                  >
                    <IconXClose
                      width={rem(20)}
                      height={rem(20)}
                    />
                  </ActionIcon>
                )
              }
            />
          </InputContainer>
          {!noPaddings && <Space h="sm" />}
        </>
      )}
    </Box>
  );
};

export default Map;

Stack Trace

TypeError ?(maps-api-v3/api/js/58/8-beta/intl/nl_ALL/map)

undefined is not an object (evaluating 'new Ja.OC')

app:///maps-api-v3/api/js/58/8-beta/intl/nl_ALL/map.js at line 123:56
@Oigen43 Oigen43 added triage me I really want to be triaged. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. labels Oct 14, 2024
@usefulthink
Copy link
Contributor

This could be either a problem within the maps API or an issue in your code, but it seems unlikely that this has something to do with the @googlemaps/js-api-loader package.

To further diagnose the issue, can you provide a full stack trace of the error so we can see where in your code the error originated?

@usefulthink usefulthink added needs more info This issue needs more information from the customer to proceed. and removed type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. triage me I really want to be triaged. labels Oct 18, 2024
@AlejandroRM-DEV
Copy link

AlejandroRM-DEV commented Dec 4, 2024

Today we had a similar case, and it only happens when the traffic is turned on.

Captura de pantalla 2024-12-04 a la(s) 4 04 34 p m

Captura de pantalla 2024-12-04 a la(s) 4 07 17 p m

@AlejandroRM-DEV
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs more info This issue needs more information from the customer to proceed.
Projects
None yet
Development

No branches or pull requests

3 participants