我的目标是当用户选择地址建议时从
google-maps-react
获取经纬度后,将 react-places-autocomplete
地图平移到纬度位置。
我在从子功能组件设置映射的 ref 时遇到困难,以便我可以在父功能组件中调用
map.panTo(location)
。
以下是我的 Google-Maps 和 PlaceAutoComplete 子组件:
import React, { useEffect } from 'react';
import { Map, GoogleApiWrapper, Marker } from 'google-maps-react';
import { FormGroup, Label, Input, Spinner, Container, Row, Col } from 'reactstrap';
import PlacesAutocomplete from 'react-places-autocomplete';
const InputAndMap = React.forwardRef((props, ref) => {
return (
<div>
<PlacesAutocomplete
value={props.address}
onChange={props.handleInputChange}
onSelect={props.handleInputSelect}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<FormGroup>
<Label for="exampleSearch">Search Address</Label>
<Input
{...getInputProps({
className: 'location-search-input',
})}
type="search"
name="search"
id="exampleSearch"
placeholder="Enter Store Location"
/>
</FormGroup>
<div className="autocomplete-dropdown-container">
{loading && (
<div>
<Spinner size="sm" color="primary" />
Loading...
</div>
)}
{suggestions.map(suggestion => {
const className = suggestion.active ? 'suggestion-item--active' : 'suggestion-item';
const style = suggestion.active
? { backgroundColor: '#007bff', cursor: 'pointer', color: 'white' }
: { backgroundColor: '#ffffff', cursor: 'pointer' };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style,
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
<Row className="mb-3" style={{ width: '100%', height: '200px' }}>
<Col>
<Map
id="google-map"
ref={ref} // <<=== setting ref here
style={{ width: '100%', height: '200px' }}
google={props.google}
zoom={8}
initialCenter={{ lat: 47.444, lng: -122.176 }}
onClick={(t, map, e) => props.updateMarker(e.latLng, map)}
>
{props.markerLatLong && <Marker position={props.markerLatLong} />}
</Map>
</Col>
</Row>
</div>
);
});
export default GoogleApiWrapper({
apiKey: process.env.REACT_APP_GOOGLE_API_KEY,
libraries: ['places'],
})(InputAndMap);
这是我的父组件,我想在其中调用地图 panto 函数。
import React, { useState, useEffect } from 'react';
import { Button, Form, Spinner, Container } from 'reactstrap';
import { Redirect } from 'react-router-dom';
import { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
import firebase from 'firebase/app';
import NavBarMenu from '../components/NavBarMenu';
import InputAndMap from '../components/InputAndMap';
import fire from '../config/fire';
function StoreScreen(props) {
const [isLoading, setIsLoading] = useState(false);
const [markerLatLong, setMarkerLatLong] = useState(null);
const [city, setCity] = useState('');
const [address, setAddress] = useState('');
const [redirect, setRedirect] = useState(false);
const ref = React.createRef();
const handleInputChange = address => {
setAddress(address);
};
const handleInputSelect = address => {
setAddress(address);
geocodeByAddress(address)
.then(results => {
processCity(results);
getLatLng(results[0])
.then(latLng => {
console.log('Success', latLng);
console.log(ref);// ==============> this return {current: null}
// ref.current.panTo(latLng);// ==> So I am unable to call this
})
.catch(error => console.error('Error', error));
})
.catch(error => console.error('Error', error));
};
return (
<div>
<NavBarMenu isShopKeeper />
<Container className="h-100">
<Form onSubmit={handleSubmit}>
<h5 className="text-center">Add Store</h5>
<InputAndMap
ref={ref}
markerLatLong={markerLatLong}
updateMarker={updateMarker}
handleInputChange={handleInputChange}
handleInputSelect={handleInputSelect}
address={address}
/>
{isLoading ? (
<div className="row mx-auto justify-content-center align-items-center flex-column">
<Spinner color="secondary" />
</div>
) : (
<Button
disabled={!markerLatLong || !city || !address}
className="mb-4"
color="primary"
size="lg"
block
>
Add Store
</Button>
)}
</Form>
</Container>
</div>
);
}
export default StoreScreen;
Map.panTo 将地图中心更改为 Maps JavaScript API 中给定的 LatLng。由于您使用的是
google-maps-react
库,因此您可以使用反应状态作为该库的 center 参数 的值,以在每次状态更改时更改地图中心的值。在下面的示例代码中,我使用了 React-places-autocomplete 的 入门文档 中的代码,并将其与简单的 google-maps-react 代码合并。
这是我如何声明当前具有值的中心的状态:
state = {
center: {
lat: 40.854885,
lng: -88.081807
},
address: ""
};
这是来自
react-places-autocomplete
库的handleSelect 事件,它对自动完成中选定的位置进行地理编码。然后你可以看到我将中心的状态设置为地理编码地址的 latLng。
handleSelect = address => {
geocodeByAddress(address)
.then(results => getLatLng(results[0]))
.then(latLng => this.setState({ center: latLng }))
.catch(error => console.error("Error", error));
};
这是我如何调用 google-maps-react 库的 Map 组件,其中 center 参数的值是名为 center 的州的值。
<Map
className="map"
google={this.props.google}
onClick={this.onMapClicked}
center={this.state.center}
style={{ height: "100%", position: "relative", width: "100%" }}
zoom={13}
/>
这是一个完整的代码片段和工作代码,说明我如何合并您用来在每次从自动完成中选择地址时更改地图中心的两个库:
import React, { Component } from "react";
import { Map, GoogleApiWrapper } from "google-maps-react";
import PlacesAutocomplete, {
geocodeByAddress,
getLatLng
} from "react-places-autocomplete";
export class MapContainer extends Component {
state = {
center: {
lat: 40.854885,
lng: -88.081807
},
address: ""
};
handleChange = address => {
this.setState({ address });
};
handleSelect = address => {
geocodeByAddress(address)
.then(results => getLatLng(results[0]))
.then(latLng => this.setState({ center: latLng }))
.catch(error => console.error("Error", error));
};
render() {
if (!this.props.loaded) return <div>Loading...</div>;
return (
<div>
<PlacesAutocomplete
value={this.state.address}
onChange={this.handleChange}
onSelect={this.handleSelect}
>
{({
getInputProps,
suggestions,
getSuggestionItemProps,
loading
}) => (
<div>
<input
{...getInputProps({
placeholder: "Search Places ...",
className: "location-search-input"
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map(suggestion => {
const className = suggestion.active
? "suggestion-item--active"
: "suggestion-item";
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: "#fafafa", cursor: "pointer" }
: { backgroundColor: "#ffffff", cursor: "pointer" };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
<Map
className="map"
google={this.props.google}
center={this.state.center}
style={{ height: "100%", position: "relative", width: "100%" }}
zoom={13}
/>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: "YOUR_API_KEY"
})(MapContainer);
const [map, setMap] = useState(null);
const [currentLocation, setCurrentLocation] = useState(undefined);
const onMapClick = (e) => {
map.panTo(e.latLng);
setTimeout(() => {
setCurrentLocation({
lat: e.latLng.lat(),
lng: e.latLng.lng(),
});
}, 600);
};
<GoogleMap
onDblClick={onMapClick}
onLoad={(map) => {
setMap(map);
}}
>
地图实例在onLoad函数中可用。我将其保存到 state 中,以便稍后可以在 onDblClick 中访问它。此外,setTimeout 允许您在单击的位置放置标记之前平移到该位置。不确定你的意图是什么,但对我来说,我双击设置了一个标记,但市场被放置在地图上,并且地图的中心在缓慢平移发生之前正在移动。我知道这个解决方案可能不是最佳的,但它有效。
最近我有同样的任务来访问 NextJS (React) 项目上的 Google 地图的参考。
在尝试不同的方法(将状态保存在钩子上、将其从一个组件传递到另一个组件、使用 redux 等)之后,我找到了一种有效的方法。
我需要从另一个组件(公司列表)访问mapRef,当用户单击任何列表项时,地图应平移到公司位置。
我使用React的useAppContext将ref保存为全局状态
const [googleMapRef, setGoogleMapRef] = useState<GoogleMapProps | null>();
从 useAppContext 导入
const { googleMapRef, setGoogleMapRef } = useAppContext();
地图加载时设置参考
const onGoogleApiLoaded = ({ map, maps }: any) => {
console.log("Google Maps API Loaded:");
mapRef.current = map;
setGoogleMapRef(map);
};
现在您可以使用 googleMapRef 从任何您想要的组件中进行平移。
const { googleMapRef, setGoogleMapRef } = useAppContext();
const onListItemClick = (lat: number, lng: number) => {
googleMapRef?.panTo({ lat, lng });
}
希望它对您或将来的任何人有帮助🙏🏻