为你的React项目接入高德地图
最近在写一个项目的时候,需要使用地图来选取点位信息
使用高德地图
在技术选型上面,我们使用Antd推荐的高德地图,并支持React的组件化
npm install --save react-amap
去高德地图申请key和密钥
添加key和安全密钥
给Map添加key
<Map
amapkey={MAP_KEY}
plugins={plugins}
events={this.handleMapEvents}
zoom={17}
center={[lng, lat]}
doubleClickZoom={false}
>
添加安全密钥
我们在开发环境下直接使用script
标签添加安全密钥
<script type="text/javascript">
window._AMapSecurityConfig = { securityJsCode:'您申请的安全密钥', }
</script>
在React脚手架中,我们可以在index.html
文件中添加script标签。但是我们使用的是Umi项目,没有index.html
,我们可以更改Umi默认模板,详见:修改默认模板
- 在pages文件夹下面添加
document.ejs
文件 - 添加密钥
创建地图组件
我们最后实现的效果可能会是这个样子
我们要实现的部分主要分成三个
- 搜索框的实现,包括搜索提示
- 地图展示部分,功能有根据根据经纬度进行定位,通过点击地图来逆编码来获得地址
- 搜索结果的基本信息展示
我们在设计组件的时候,可以从功能上进行拆分,并考虑到组件复用的问题,尽量让组件原子化,只干一件事情。还可以将组件分成容器组件(只管理数据)和渲染组件(只管理视图)进行分离,并尽量保持组件的无状态,无state
划分组件
根据上面的分析,我们可以将搜索框单独设置为一个组件,并且可以复用在其他需要搜索地址的地方。将地图的展示单独设置成为一个组件,只需要接受到经纬度就好可以展示相应的信息了。搜索结果也可以单独展示出来就好了。
同时,在搜索框输入地址的时候,我们可以改变地图组件的状态;点击地图中的点位的时候,也可以改变输入框中的值。所以我们可以创建一个容器组件来管理两者互通的数据,即经纬度和地址名称
示意图:
搜索组件编码
JavaScript API支持搜索服务脱离地图使用,即使用搜索服务不再需要先实例化地图。您可通AMap.plugin
方法,加载需要的服务。同时JavaScript API将原有的通过事件监听获得服务查询结果,修改为通过方法的回调函数获得服务查询结果。
由于我们每次改变输入框中的值,就会触发一次请求,所以我们可以在输入框中加一个防抖函数
import React, { Component } from 'react';
import { Select } from 'antd';
const { Option } = Select;
class SearchAddress extends Component {
constructor(props) {
super(props);
this.state = {
positionList:[],
}
this.handleMapEvents = {
created: () => {
window.AMap.plugin('AMap.PlaceSearch', () => {
new window.AMap.PlaceSearch({
pageSize: 10,
pageIndex: 1,
});
})
},
}
}
onSearch =(val)=>{
const place = new window.AMap.PlaceSearch({
pageSize: 10,
pageIndex: 1,
city: '绵阳',
})
place.search(val,(status, result)=>{
if(status === 'complete' && result.info === 'OK'){
const {poiList:{pois}}=result
if(pois && Array.isArray(pois)) {
this.setState({
positionList:pois
})
}
}
})
}
debounce = (fn,time) => {
let timerId = null;
return function (val) {
if(timerId) {
clearTimeout(timerId);
timerId = null;
}
timerId = setTimeout(() => {
fn.call(this,val)
},time)
}
}
// 选中Seleect框中触发回调,改变父组件的position和addressName
onChange = (id) => {
const { positionList } = this.state;
const {changeAddressName,changePosition} = this.props;
for(const item of positionList) {
const { name:itemName,id:itemId } = item;
if(itemId === id) {
const {location : {lng,lat}} = item;
const position = {lng,lat};
changePosition(position)
changeAddressName(itemName)
}
}
}
render() {
const {addressName} = this.props
const {positionList} = this.state;
return (
<div>
<Select
value={addressName}
style={{ width: 400 }}
showSearch
placeholder="请输入地址"
onSearch={this.debounce((val) => this.onSearch(val),300)}
onChange={this.onChange}
optionFilterProp="children"
>
{
positionList.map( item=>
<Option key={item.id} value={item.id}>{item.name}</Option>
)
}
</Select>
</div>
);
}
}
export default SearchAddress;
地图组件编码实现
当我们点击地图时,只能通过click事件得到经纬度,而得不到地址名。经纬度对于用户是没有用的, 所以我们使用逆编码来获得地址名称
我们还可以使用static propTypes和defaultProps来规定props传入值的类型和默认值
static propTypes = {
position:PropTypes.object,
plugins: PropTypes.array,
};
static defaultProps = {
position :{
lng:104.679127,
lat:31.467673,
},
plugins: ["ToolBar", 'Scale']
};
地图组件:
import React, { Component, Fragment } from 'react';
import {Map, Marker} from 'react-amap';
import PropTypes from 'prop-types';
import { message } from 'antd';
import { MAP_KEY } from '@/utils/Enum';
class AdvancedMap extends Component {
static propTypes = {
position:PropTypes.object,
plugins: PropTypes.array,
};
static defaultProps = {
position :{
lng:104.679127,
lat:31.467673,
},
plugins: ["ToolBar", 'Scale']
};
constructor(props) {
super(props);
this.handleMapEvents = {
created: () => {
window.AMap.plugin('AMap.PlaceSearch', () => {
new window.AMap.PlaceSearch({
pageSize: 10,
pageIndex: 1,
});
})
window.AMap.plugin('AMap.Geocoder', () =>{
new window.AMap.Geocoder({
city: '0816'
})
})
},
click: (e) => {
const { lnglat:lnglatObj,lnglat: {lng,lat} } = e;
const { changePosition,changeAddressName } = this.props;
const lnglatArr = [lng,lat]
const geocoder = new window.AMap.Geocoder({
city: '0816'
})
changePosition(lnglatObj)
geocoder.getAddress(lnglatArr, (status, result) => {
if (status === 'complete' && result.info === 'OK') {
console.log(result,'result');
const { regeocode :{formattedAddress}} = result;
changeAddressName(formattedAddress)
message.success(formattedAddress)
}
})
}
}
}
render() {
const {position: { lng,lat }, plugins} = this.props;
return (
<Fragment>
<Map
amapkey={MAP_KEY}
plugins={plugins}
events={this.handleMapEvents}
zoom={17}
center={[lng, lat]}
doubleClickZoom={false}
>
<Marker position={[lng, lat]} />
</Map>
</Fragment>
);
}
}
export default AdvancedMap;
父级容器
使用父级容器主要是管理数据(position,addressName),并传递改变数据(position,addressName)的方法给子组件,子组件调用父组件传递的函数,就可以更改父组件的数据(参考父子组件参数)
import React, { Component } from 'react';
import { Col, Input, Row } from 'antd';
import SearchAddress from '@/pages/Map/SearchAddress';
import AdvancedMap from '@/pages/Map/AdvancedMap';
class Index extends Component {
constructor(props) {
super(props);
this.state = {
position: {
lng:104.679127,
lat:31.467673,
},
addressName:''
}
}
changePosition = (value) => {
this.setState({
position:value
})
}
changeAddressName = (value) => {
this.setState({
addressName:value
})
}
render() {
const {position,addressName} = this.state;
return (
<div style={{ width: '100%', height: '500px' }}>
<Row gutter={24}>
<Col span={8}>
<SearchAddress
changePosition={this.changePosition}
changeAddressName={this.changeAddressName}
addressName={addressName}
/>
</Col>
</Row>
<br />
<AdvancedMap
position={position}
changePosition={this.changePosition}
changeAddressName={this.changeAddressName}
/>
</div>
);
}
}
export default Index;
效果
后续还要再完善一下,比如规定好每个组件的输入输出,防止使用的时候出错
转载自:https://juejin.cn/post/7208188059632271421