JustFly1984 / react-google-maps-api

React Google Maps API
MIT License
1.76k stars 426 forks source link

MarkerClusterer children only accepts a single element #3170

Open cjones26 opened 1 year ago

cjones26 commented 1 year ago

Issue template

You can donate or became a sponsor https://opencollective.com/react-google-maps-api#category-CONTRIBUTE

If you want to ask question, please ask it in Github Discussions, Spectrum.chat or Slack channel

Please do not post unformatted code into issues, and please do not ask questions. Only real issues, PR's or feature requests are allowed. Minimal reproduction in codesandbox.io is required.

Please provide an explanation of the issue

After updating our @react-google-maps/api package from 2.8.1 -> 2.17.1, we've discovered that our previous code was throwing TypeScript issues due to the children component no longer accepting an array of JSX.Element, as seen here: https://github.com/JustFly1984/react-google-maps-api/blob/9840eee1f3f09391c23fe4117e8b1e5592c94cdc/packages/react-google-maps-api/src/components/addons/MarkerClusterer.tsx#L102.

This is inconsistent with the example in the documentation here:

const { GoogleMap, LoadScript, MarkerClusterer, Marker } = require('../../')
const ScriptLoaded = require('../../docs/ScriptLoaded').default

const mapContainerStyle = {
  height: '400px',
  width: '800px',
}

const center = { lat: -28.024, lng: 140.887 }

const locations = [
  { lat: -31.56391, lng: 147.154312 },
  { lat: -33.718234, lng: 150.363181 },
  { lat: -33.727111, lng: 150.371124 },
  { lat: -33.848588, lng: 151.209834 },
  { lat: -33.851702, lng: 151.216968 },
  { lat: -34.671264, lng: 150.863657 },
  { lat: -35.304724, lng: 148.662905 },
  { lat: -36.817685, lng: 175.699196 },
  { lat: -36.828611, lng: 175.790222 },
  { lat: -37.75, lng: 145.116667 },
  { lat: -37.759859, lng: 145.128708 },
  { lat: -37.765015, lng: 145.133858 },
  { lat: -37.770104, lng: 145.143299 },
  { lat: -37.7737, lng: 145.145187 },
  { lat: -37.774785, lng: 145.137978 },
  { lat: -37.819616, lng: 144.968119 },
  { lat: -38.330766, lng: 144.695692 },
  { lat: -39.927193, lng: 175.053218 },
  { lat: -41.330162, lng: 174.865694 },
  { lat: -42.734358, lng: 147.439506 },
  { lat: -42.734358, lng: 147.501315 },
  { lat: -42.735258, lng: 147.438 },
  { lat: -43.999792, lng: 170.463352 },
]

const options = {
  imagePath:
    'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m', // so you must have m1.png, m2.png, m3.png, m4.png, m5.png and m6.png in that folder
}

function createKey(location) {
  return location.lat + location.lng
}

const MapWithMarkerClusterer = () => {
  return (
    <ScriptLoaded>
      <GoogleMap id='marker-example' mapContainerStyle={mapContainerStyle} zoom={3} center={center}>
        <MarkerClusterer options={options}>
          {(clusterer) =>
            locations.map((location) => (
              <Marker key={createKey(location)} position={location} clusterer={clusterer} />
            ))
          }
        </MarkerClusterer>
      </GoogleMap>
    </ScriptLoaded>
  )
}

Your Environment

os: Mac

node --version: 16.15.0

react version: 17.0.2

webpack version: 5.73.0

@babel version: 7.18.5

@react-google-maps/api version: 2.17.1

How does it behave?

It throws a TypeScript error as shown below:

src/components/GoogleMapContainer/GoogleMapContainer.tsx:152:13 - error TS2769: No overload matches this call.
  Overload 1 of 2, '(props: MarkerClustererProps | Readonly<MarkerClustererProps>): 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'.

152             pinList.map((pin) => (
                ~~~~~~~~~~~~~~~~~~~~~~
153               <Marker
    ~~~~~~~~~~~~~~~~~~~~~
...
173               </Marker>
    ~~~~~~~~~~~~~~~~~~~~~~~
174             ))
    ~~~~~~~~~~~~~~

Found 1 error in src/components/GoogleMapContainer/GoogleMapContainer.tsx:152

If we attempt to map elements directly as a child of the MarkerClustererComponent as shown below:

<MarkerClusterer
  styles={[
    {
      url: MultipleLocationsPinIcon as string,
      width: 40,
      height: 37,
      textColor: '#FFF',
    },
  ]}
  {...markerClustererProps}
>
  {(clusterer) => (
    <>
      {pinList.map((pin) => (
        <Marker
          clusterer={clusterer}
          key={pin.id}
          position={{
            lat: pin.coordinate.latitude,
            lng: pin.coordinate.longitude,
          }}
          onClick={(): void => markerClickHandler(pin)}
          label={markerProps?.label ?? markerProps?.getLabel?.(pin)}
          icon={
            markerProps?.getIcon?.(pin) ?? {
              url: (pin.selected
                ? markerProps?.activeIcon ?? SingleLocationSelectedPin
                : markerProps?.icon ?? SingleLocationPin) as string,
              anchor: new google.maps.Point(5, 58),
            }
          }
          {...markerProps}
        >
          {markerProps?.children?.(pin)}
        </Marker>
      ))}
    </>
  )}
</MarkerClusterer>

How should it behave correctly?

It should properly accept either JSX.Element or JSX.Element[].

Basic implementation of incorrect behavior in codesandbox.com

Apologies I do not have time to implement this now, but will do it in a bit when I do.

JustFly1984 commented 1 year ago

you should wrap array in fragment tag

cjones26 commented 1 year ago

That's exactly what I ended up doing, just wanted to bring it to attention as it seems like it's a breaking change which should be called out.