google-mapsgoogle-maps-api-3google-maps-markersmarkerclusterergoogle-maps-react

Google Maps MarkerClusterer in React with @react-google-maps/api


I am getting an error while I want to implement MarkerClusterer from @react-google-maps/api.

The error that I am getting is

No overload matches this call. Overload 1 of 2, '(props: MarkerClustererProps | Readonly): ClustererComponent', gave the following error. Type 'Element[]' is missing the following properties from type 'ReactElement<any, any>': type, props, key Overload 2 of 2, '(props: MarkerClustererProps, context: any): ClustererComponent', gave the following error. Type 'Element[]' is not assignable to type 'Element'.ts(2769) index.d.ts(378, 15): The expected type comes from the return type of this signature. index.d.ts(378, 15): The expected type comes from the return type of this signature.

You can check the codes below. (I am sharing only the relevant parts of my whole code)


import React, { useEffect, useState, useRef } from 'react';
import { GoogleMap, useJsApiLoader, Marker, MARKER_LAYER, OverlayView, MarkerClusterer } from '@react-google-maps/api';
import './app.css';
import PlacesAutocomplete from './components/PlacesAutocomplete';


const containerStyle = {
    width: '100vw',
    height: '100vh',
};


const App = () => {
    const [center, setCenter] = useState({ lat: 39.015137, lng: 34.97953 });
    const [zoom, setZoom] = useState(6);
    const [markers, setMarkers] = useState<{ id: string; name: string; avatar: string; latLng?: google.maps.LatLng }[]>([]);
    const [users, setUsers] = useState<{ id: string; name: string; role: string; avatar: string; color: string; latLng: boolean }[]>([]);

    const mounted = useRef(false);
    const markerRef = useRef<Marker>(null);

    const { isLoaded } = useJsApiLoader({
        id: 'google-map-script',
        googleMapsApiKey: 'API_KEY',
        libraries: ['places'],
    });

    let markersInfo: { id: string; name: string; avatar: string; latLng?: google.maps.LatLng }[] = [];

    return isLoaded ? (
        <>
            <PlacesAutocomplete setCenter={setCenter} setZoom={setZoom} />
            <GoogleMap mapContainerStyle={containerStyle} center={center} zoom={zoom} onClick={handleAddMarker}>
                <>
                    <OverlayView
                        position={{ lat: -34.397, lng: 150.644 }}
                        mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
                    >
                        <>
                            <MarkerClusterer>
                              {clusterer => markers?.map((obj: any, i) => (
                                <Marker
                                    onClick={handleMarkerHover}
                                    ref={markerRef}
                                    key={i}
                                    position={obj.latLng}
                                    label={{
                                        // text: obj.name,
                                        text: obj.avatar
                                            ? obj.name
                                            : obj.name
                                                  .split(' ')
                                                  .map((name: string) => name[0])
                                                  .join('')
                                                  .toUpperCase(),
                                        className: obj.avatar ? `marker-label-with-avatar` : 'marker-label-without-avatar',
                                    }}
                                    icon={{
                                        scaledSize: new google.maps.Size(50, 50),
                                        url: obj.avatar ? obj.avatar + '#custom_marker' : 'avatar_default.png' + '#custom_marker_w/o_avatar',
                                        origin: new google.maps.Point(0, 0),
                                        anchor: new google.maps.Point(25, 50),
                                    }}
                                    animation={google.maps.Animation.DROP}
                                    // draggable={true}
                                />
                            ))}
                            </MarkerClusterer>
                        </>
                    </OverlayView>
                </>
            </GoogleMap>
        </>
    ) : (
        <></>
    );
};

export default React.memo(App); 


Solution

  • The expected return from function(clusterer: Clusterer) is a JSX.Element. So I would suggest the following changes:

              <MarkerClusterer>
            {(clusterer) => (
              <div>
                {markers?.map((obj: any, i) => (
                  <Marker
                    onClick={handleMarkerHover}
                    ref={markerRef}
                    key={i}
                    position={obj.latLng}
                    label={{
                      // text: obj.name,
                      text: obj.avatar
                        ? obj.name
                        : obj.name
                            .split(' ')
                            .map((name: string) => name[0])
                            .join('')
                            .toUpperCase(),
                      className: obj.avatar ? `marker-label-with-avatar` : 'marker-label-without-avatar',
                    }}
                    icon={{
                      scaledSize: new google.maps.Size(50, 50),
                      url: obj.avatar
                        ? obj.avatar + '#custom_marker'
                        : 'avatar_default.png' + '#custom_marker_w/o_avatar',
                      origin: new google.maps.Point(0, 0),
                      anchor: new google.maps.Point(25, 50),
                    }}
                    animation={google.maps.Animation.DROP}
                    // draggable={true}
                  />
                ))}
              </div>
            )}
          </MarkerClusterer>