Peter Reshetin
Peter Reshetin

Reputation: 925

Center AND zoom by prop in react-google-maps?

I am building a Zillow-like map with real estate property points in page body, and filters in page header.

What I need is to center AND zoom react-google-maps map, when selecting Google place from external Google places filter. Currently I am able to center map on place select, but not zoom in (that is because i need to refetch data on every zoom in/out, so need to control zoom, see code below).

I tried to use fitBounds from here, but ref in my code is always undefined.

I have API that fetches points, based on filters, with params like this:

  viewPort: {
    northEast: { lat: 57.04834755670983, lng: 24.3255340332031 },
    southWest: { lat: 56.83635114109457, lng: 23.93826596679685 }
  },
  priceMin: 0,
  priceMax: 500,
  propType: "RENT",
  numRooms: 3,
...

In header section there is also address filter input, that has Google places autocomplete. When selected, Google provides info about place:

"geometry" : {
... 

"viewport" : {
  "northeast" : {
    "lat" : 56.95662478029149,
    "lng" : 24.2054917802915
  },
  "southwest" : {
      "lat" : 56.9539268197085,
      "lng" : 24.2027938197085
    }
}
  ...

Here is my Map component:

import React, { Component } from 'react';

import { compose, withProps, withState, withHandlers } from "recompose";
import {
  withGoogleMap,
  GoogleMap,
  Marker,
} from "react-google-maps";
import { MarkerClusterer } from "react-google-maps/lib/components/addons/MarkerClusterer";

const defaultMapOptions = {
  fullscreenControl: false,
  streetViewControl: false,
};

const PropertyMap = compose(
  withProps({
    loadingElement: <div>Loading...</div>,
    containerElement: <div style={{ height: "500px" }} />,
    mapElement: <div style={{ height: `100%` }} />,
  }),
  withState('zoom', 'onZoomChange', 11),
  withHandlers(() => {
    const refs = {
      map: undefined,
    }

    return {
      onMapMounted: () => ref => {
        refs.map = ref
      },
      onZoomChanged: ({ onZoomChange, onMapBoundsChange }) => () => {
        onZoomChange(refs.map.getZoom())
        onMapBoundsChange(refs.map.getBounds());

      },
      onDragEnd: ({ onMapBoundsChange }) => () => {
        onMapBoundsChange(refs.map.getBounds());
      },
      onMarkerClustererClick: () => (markerClusterer) => {
        const clickedMarkers = markerClusterer.getMarkers()
        console.log(`Current clicked markers length: ${clickedMarkers.length}`)
        console.log(clickedMarkers)
      }
    }
  }),
  withGoogleMap
)(props =>
  <GoogleMap
    defaultCenter={props.defaultCenter ? props.defaultCenter : { lat: 56.9425, lng: 24.1319 }}
    zoom={props.zoom}
    ref={props.onMapMounted}
    onZoomChanged={props.onZoomChanged}
    onDragEnd={props.onDragEnd}
    defaultOptions={defaultMapOptions}
  >
    <MarkerClusterer
      onClick={props.onMarkerClustererClick}
      averageCenter
      enableRetinaIcons
      gridSize={60}
      key={props.mapUid}
    >
      {props.features !== null && props.features.map(feature => {
        const coord = { lng: feature.geometry.coordinates[0], lat: feature.geometry.coordinates[1] };
        return <Marker key={feature.id} position={coord} />;
      })}

    </MarkerClusterer>
  </GoogleMap>
);

export default PropertyMap;

So I think I would like to make it possible to pass zoom param in props, like this:

<PropertyMap
  mapUid={this.state.mapUid}
  features={this.state.features}
  key={this.state.defaultCenter ? `${this.state.defaultCenter.lat}` : '1'}
  defaultCenter={this.state.defaultCenter}
  defaultZoom={calculateZoomFromViewPort(this.state.viewPort)}
  onMapBoundsChange={this.handleMapBoundsChange}
/>

This way I could calculateZoomFromViewPort(this.state.viewPort), but then I need to somehow set it to withState zoom default value.

So what I need is some how pass default zoom value to withState('zoom', 'onZoomChange', 11), (11 is hardcoded, currently). But I am new to recompose package, but is it heavily used in react-google-maps package examples.

If you have other ideas, how to zoom and center map based on viewport & center params from Google place, please share!

Upvotes: 1

Views: 10690

Answers (1)

Sakhi Mansoor
Sakhi Mansoor

Reputation: 8102

fitbounds() is the recommended way to achieve auto zoom. Remember refs always created after first render of the component. I have made changes in your code by adding fitBounds. There might be few syntax errors as I prefer to use react-google-maps like class based utility wrapper. It's easier to manage with redux and component lifecycles. Below is the latest code:

import React, { Component } from 'react';

import { compose, withProps, withState, withHandlers } from "recompose";
import {
  withGoogleMap,
  GoogleMap,
  Marker,
} from "react-google-maps";
import { MarkerClusterer } from "react-google-maps/lib/components/addons/MarkerClusterer";

const defaultMapOptions = {
  fullscreenControl: false,
  streetViewControl: false,
};

const PropertyMap = compose(
  withProps({
    loadingElement: <div>Loading...</div>,
    containerElement: <div style={{ height: "500px" }} />,
    mapElement: <div style={{ height: `100%` }} />,
  }),
  withState('zoom', 'onZoomChange', 11),
  withHandlers(() => {
    const refs = {
      map: undefined,
    }

    return {

     onMarkersUpdate: ()=> () => {
     if(refs.map){
      const mapBounds = new google.maps.LatLngBounds();
      //just add markers position into mapBounds
       markers.forEach((marker)=>{
        mapBounds.extend(marker.position)
      });

       this.refs.map.fitBounds(mapBbounds);
       }
     },
      onZoomChanged: ({ onZoomChange, onMapBoundsChange }) => () => {
        onZoomChange(refs.map.getZoom())
        onMapBoundsChange(refs.map.getBounds());

      },
      onDragEnd: ({ onMapBoundsChange }) => () => {
        onMapBoundsChange(refs.map.getBounds());
      },
      onMarkerClustererClick: () => (markerClusterer) => {
        const clickedMarkers = markerClusterer.getMarkers()
        console.log(`Current clicked markers length: ${clickedMarkers.length}`)
        console.log(clickedMarkers)
      }
    }
  }),
  withGoogleMap
)(props =>
  <GoogleMap
    defaultCenter={props.defaultCenter ? props.defaultCenter : { lat: 56.9425, lng: 24.1319 }}
    zoom={props.zoom}
    ref='map'
    onZoomChanged={props.onZoomChanged}
    onDragEnd={props.onDragEnd}
    defaultOptions={defaultMapOptions}
  >
    {props.onMarkersUpdate()}
    <MarkerClusterer
      onClick={props.onMarkerClustererClick}
      averageCenter
      enableRetinaIcons
      gridSize={60}
      key={props.mapUid}
    >
      {props.features !== null && props.features.map(feature => {
        const coord = { lng: feature.geometry.coordinates[0], lat: feature.geometry.coordinates[1] };
        return <Marker key={feature.id} position={coord} />;
      })}

    </MarkerClusterer>
  </GoogleMap>
);

export default PropertyMap;

Please feel free to react out me. If you make a codesandbox for this. It will be easier to fix syntax errors. And Don't miss out fitbounds(). It makes things easier for us.

Upvotes: 1

Related Questions