How to use fitBounds in react-google-map once you have bounds

14,349

Solution 1

You can access fitBounds by adding a ref to the DOM element that google-maps-react generates.

first, add a ref: <GoogleMap ref={(ref) => { this.map = ref; }}>...</GoogleMap>

Once the component is mounted, you will be able to call fitBounds using the ref. this.map.fitBounds(bounds)

Solution 2

You can use fitbounds in componentDidMount or render function

add ref to GoogleMap and apply fitbounds <GoogleMap ref={map => map && map.fitBounds(bounds)}> .... </GoogleMap>

Solution 3

You can assign a ref to the GoogleMap Component, then call the fitBounds() function using a lifecycle hook.

import React, { useRef, useEffect } from 'react';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps";
import { GOOGLE_API_KEY } from '../../config/config';


// =======================================================================
//  GOOGLE MAPS
// =======================================================================
const RegularMap = withScriptjs(
  withGoogleMap(
    ({ defaultCenter, markers }) => {
      const mapRef = useRef(null);

      // Fit bounds function
      const fitBounds = () => {
        const bounds = new window.google.maps.LatLngBounds();
        markers.map(item => {
          bounds.extend(item.position);
          return item.id
        });
        mapRef.current.fitBounds(bounds);
      };

      // Fit bounds on mount, and when the markers change
      useEffect(() => {
        fitBounds();
      }, [markers]);


      return (
        <GoogleMap ref={mapRef} defaultCenter={defaultCenter}>
          {markers.map(
            ({ position }, index) => <Marker key={`marker_${index}`} position={position} />
          )}
        </GoogleMap>
      );
    })
);


// =======================================================================
//  THE MAIN COMPONENT
// =======================================================================

const MapView = () => {
  const markers = [
    { position: { lat: 37.778519, lng: -122.405640 } },
    { position: { lat: 6.4454594, lng: 3.449074 } }
  ];

  return (
      <RegularMap
        defaultCenter={{ lat: 6.4454594, lng: 3.449074 }}
        markers={markers}
        googleMapURL={`https://maps.googleapis.com/maps/api/js?key=${GOOGLE_API_KEY}`}
        loadingElement={<div className="loader" />}
        containerElement={<div className="mapContainer" style={{ height: "400px" }} />}
        mapElement={<div className="map" style={{ height: '100%' }} />}
      />
  );
}

export default MapView;
Share:
14,349
Taylor Austin
Author by

Taylor Austin

Updated on June 04, 2022

Comments

  • Taylor Austin
    Taylor Austin almost 2 years

    Where exactly do I call fitBounds or map.fitbounds. I am confused where to put it once I have the bounds or how to use it. I have set the bounds to a local state of bounds.

    I have looked at this post here react-google-maps: how to use fitBounds, panBy, panTo, panToBounds public APIs? and either I just do it different or something because it does not make much since to me.

    I have created a bounds const and then each time I make a marker I have added each of those to the bounds doing bounds.extends(Marker in here)

    The code is a little messy so I will point out where I do this in it. Inside the filterItemsByRadius is where I create and set the bounds. At the end of the map function I think set the state of bounds to the bounds.

    /* global google */
    import { default as React, Component } from 'react';
    import raf from 'raf';
    import canUseDOM from 'can-use-dom';
    import { connect } from 'react-redux';
    import { Link } from 'react-router';
    import {
      withGoogleMap,
      GoogleMap,
      Circle,
      InfoWindow,
      Marker,
      withScriptjs,
    } from 'react-google-maps';
    import geolib from 'geolib';
    import { geolocated } from 'react-geolocated';
    import ItemList from './ItemList';
    import { Col } from 'react-bootstrap';
    
    import Paper from 'material-ui/Paper';
    import Img from 'react-image';
    import RaisedButton from 'material-ui/RaisedButton';
    import FontIcon from 'material-ui/FontIcon';
    import CreateRadius from './CreateRadius';
    import offerActions from '../../redux/actions/offerActions';
    
    const googleMapURL =
      'https://maps.googleapis.com/maps/api/js?libraries=places,geometry&key=AIzaSyA7XEFRxE4Lm28tAh44M_568fCLOP_On3k';
    
    const isJson = str => {
      try {
        JSON.parse(str);
      } catch (e) {
        return false;
      }
      return true;
    };
    
    const GeolocationGoogleMap = withScriptjs(
      withGoogleMap(props => (
        <GoogleMap defaultZoom={6} zoom={props.zoom} center={props.center} onClick={props.onMapClick}>
          {props.center && (
            <Marker
              position={props.center}
              title={"User's Location"}
              options={{ icon: require('./assets/blueDot.png') }}
            >
              {props.showCenterInfo && (
                <InfoWindow>
                  <div>User's Location</div>
                </InfoWindow>
              )}
            </Marker>
          )}
          {/* <Circle
              center={props.center}
              radius={props.zoom}
              options={{
                fillColor: 'red',
                fillOpacity: 0.2,
                strokeColor: 'red',
                strokeOpacity: 1,
                strokeWeight: 1,
              }}
            /> */}
          {props.markers.map((marker, index) => {
            const onClick = () => props.onMarkerClick(marker);
            const onCloseClick = () => props.onCloseClick(marker);
    
            return (
              <Marker
                key={index}
                position={marker.position}
                title={marker.number.toString()}
                onClick={onClick}
                options={{ icon: 'https://image.ibb.co/evMHxF/shopping_zone_marker_1.png' }}
              >
                {marker.showInfo && (
                  <InfoWindow onCloseClick={onCloseClick}>
                    <div>
                      <ItemList marker={marker} />
                    </div>
                  </InfoWindow>
                )}
              </Marker>
            );
          })}
        </GoogleMap>
      )),
    );
    
    class OfferMap extends Component {
      constructor(props) {
        super(props);
        this.state = {
          currentPosition: null,
          center: null,
          content: null,
          radius: 15, // ACZ --> put this const in config_env.
          showCenterInfo: true,
          markers: [],
          zoom: 6,
          bounds: null,
        };
    
        this.handleMarkerClick = this.handleMarkerClick.bind(this);
        this.handleCloseClick = this.handleCloseClick.bind(this);
        this.handleMapClick = this.handleMapClick.bind(this);
        this.filterItemsByRadius = this.filterItemsByRadius.bind(this);
        this.radiusChange = this.radiusChange.bind(this);
        this.zoomChange = this.zoomChange.bind(this);
      }
    
      componentWillReceiveProps(props) {
        if (props.coords && !props.coords.positionError) {
          this.setState({ center: { lat: props.coords.latitude, lng: props.coords.longitude } });
        } else {
          fetch('http://ip-api.com/json')
            .then(res => res.json())
            .then(data => {
              this.setState({ center: { lat: data.lat, lng: data.lon } });
            })
            .catch(error => {
              this.setState({ content: `Error: The Geolocation service failed (${error.message}).` });
            });
        }
        this.setState({ markers: props.items });
      }
    
      handleMapClick() {
        this.setState({ showCenterInfo: false });
      }
    
      handleMarkerClick(targetMarker) {
        this.setState({
          markers: this.state.markers.map(marker => {
            if (marker._id === targetMarker._id) {
              return {
                ...marker,
                showInfo: true,
              };
            }
            return marker;
          }),
        });
      }
    
      handleCloseClick(targetMarker) {
        this.setState({
          markers: this.state.markers.map(marker => {
            if (marker._id === targetMarker._id) {
              return {
                ...marker,
                showInfo: false,
              };
            }
            return marker;
          }),
        });
      }
    
      filterItemsByRadius(userRadius) {
        const items = this.state.markers;
        const markers = [];
        const bounds = new google.maps.LatLngBounds();
    
        items.map((item, i) => {
          let itemGeolocation;
          let itemDescription = 'NO DESCRIPTION';
          let itemThumb;
          // Should be ready - tbaustinedit
          if (item) {
            itemDescription = item.description;
            itemThumb = item.media.mediaVault[item.media.defaultIdx] || {
              mediaType: 'txt',
            };
          }
          if (item.geolocation) {
            itemGeolocation = item.geolocation.coords;
          }
          if (this.state.center) {
            const currentLocation = {
              latitude: this.state.center.lat,
              longitude: this.state.center.lng,
            };
            const distanceArr = geolib.orderByDistance(currentLocation, [itemGeolocation]);
            const miles = (distanceArr[0].distance / 1609.34).toFixed(2);
            if (miles <= userRadius) {
              const loc = new google.maps.LatLng(itemGeolocation.lat, itemGeolocation.lng);
              bounds.extends(loc);
              markers.push({
                _id: item._id,
                position: itemGeolocation,
                number: i,
                content: itemDescription,
                price: item.price,
                quantity: item.quantity,
                currency: item.currency,
                category: item.category,
                title: item.title,
                offer: item.offer,
                thumbnail: itemThumb,
                showInfo: item.showInfo || false,
              });
            }
          }
        });
        this.setState({
          bounds,
        });
        return markers;
      }
    
      radiusChange(event) {
        console.log(event.target.value);
        this.setState({
          radius: event.target.value,
        });
    
        const { filter } = this.props.offers;
        filter.radius = event.target.value;
        this.props.sortOffersNew(filter);
      }
    
      zoomChange(e) {
        console.log('value', e.target.value);
        this.setState({ zoom: Number(e.target.value) });
      }
    
      render() {
        const markers = this.filterItemsByRadius(this.state.radius);
        return (
          <Col xs={12} smOffset={0} mdOffset={0}>
            <div>
              <div style={{ fontFamily: 'Roboto', fontStyle: 'normal' }}>
                Offers within radius of: {' '}
                <input type="text" defaultValue={this.state.radius} onChange={this.radiusChange} /> {' '}
                miles
                <br />
              </div>
              {/* <CreateRadius
                radiusChange={this.radiusChange}
                numOffers={markers.length}
                initRadius={this.state.zoom}
              /> */}
            </div>
            <br />
    
            <div
              style={{
                width: '100%',
                height: '500px',
              }}
            >
              <GeolocationGoogleMap
                googleMapURL={googleMapURL}
                loadingElement={<div style={{ height: '100%' }} />}
                containerElement={<div style={{ height: '100%' }} />}
                mapElement={<div style={{ height: '100%' }} />}
                center={this.state.center}
                showCenterInfo={this.state.showCenterInfo}
                content={this.state.content}
                radius={this.state.radius}
                onMapClick={this.handleMapClick}
                onMarkerClick={this.handleMarkerClick}
                onCloseClick={this.handleCloseClick}
                markers={markers}
                zoom={this.state.zoom}
                bounds={this.state.bounds}
              />
            </div>
          </Col>
        );
      }
    }
    
    function mapStateToProps({ browser, offers }) {
      return { browser, offers };
    }
    
    const dispatchToProps = dispatch => ({
      sortOffersNew: filter => dispatch(offerActions.sortOffers(filter)),
    });
    
    export default connect(mapStateToProps, dispatchToProps)(
      geolocated({
        positionOptions: {
          enableHighAccuracy: false,
        },
        userDecisionTimeout: 5000,
      })(OfferMap),
    );
    
  • Taylor Austin
    Taylor Austin over 6 years
    Can I call this in componentDidMount or another life cycle method ?
  • Max
    Max over 6 years
    Yes, you will be able to call it in componentDidMount
  • Jabal Logian
    Jabal Logian over 2 years
    I get an error Uncaught TypeError: mapRef.current.fitBounds is not a function
  • Lucy Gu
    Lucy Gu about 2 years
    Me too! Any way to fix the fitbounds not a function?