我正在盖茨比网站上构建一个谷歌地图,该地图带有一个搜索框,用户可以搜索自己的位置。我有一个Hero
组件和Map
组件,所有功能都内置在Map
组件中;谷歌地图API、自动完成、谷歌位置等。下面是它现在的样子:
地图.tsx
import React, { useState, useRef, useCallback } from 'react';
import { GoogleMap, useLoadScript, Marker, InfoWindow } from '@react-google-maps/api';
import * as dealersData from 'assets/data/dealers.json';
import { Button } from 'components/button/Button';
import MapMarker from 'assets/images/icons/map-marker.png';
import SearchIcon from 'assets/svg/search.svg';
import usePlacesAutocomplete, { getGeocode, getLatLng } from 'use-places-autocomplete';
import {
Combobox,
ComboboxInput,
ComboboxPopover,
ComboboxList,
ComboboxOption,
} from '@reach/combobox';
import '@reach/combobox/styles.css';
import s from './Map.scss';
import MapStyles from './_MapStyles';
const libraries = ['places'];
const mapContainerStyle = {
width: '100%',
height: '100%',
};
const options = {
styles: MapStyles,
disableDefaultUI: true,
zoomControl: true,
};
interface MapProps {
location: any;
fetchedData: ReactNode;
}
export const Map = ({ location, fetchedData }: MapProps) => {
const center = {
lat: location.location.latitude,
lng: location.location.longitude,
};
const [selectedDealer, setselectedDealer] = useState(null);
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: process.env.GATSBY_GOOGLE_MAPS_API,
libraries,
});
const mapRef = useRef();
const onMapLoad = useCallback((map) => {
mapRef.current = map;
}, []);
const panTo = useCallback(({ lat, lng }) => {
mapRef.current.panTo({ lat, lng });
mapRef.current.setZoom(10);
}, []);
if (loadError) return <div className={s.map}>Error loading maps...</div>;
if (!isLoaded) return <div className={s.map}>Loading...</div>;
return (
<>
<div className={s.map}>
<div className={s.map__search}>
<Search panTo={panTo}></Search>
</div>
<GoogleMap
mapContainerStyle={mapContainerStyle}
zoom={10}
center={center}
options={options}
onLoad={onMapLoad}
>
{dealersData.features.map((dealer) => (
<Marker
key={dealer.properties.DEALER_ID}
position={{
lat: dealer.geometry.coordinates[1],
lng: dealer.geometry.coordinates[0],
}}
onClick={() => {
setselectedDealer(dealer);
}}
icon={{
url: MapMarker,
scaledSize: new window.google.maps.Size(30, 30),
}}
/>
))}
{selectedDealer && (
<InfoWindow
position={{
lat: selectedDealer.geometry.coordinates[1],
lng: selectedDealer.geometry.coordinates[0],
}}
onCloseClick={() => {
setselectedDealer(null);
}}
>
<div className={s.dealer}>
<h2 className={s.dealer__name}>
{selectedDealer.properties.NAME}
<span className={s.phone}>{selectedDealer.properties.PHONE_NUMBER}</span>
</h2>
<div className={s.dealer__info}>
<p className={s.address}>{selectedDealer.properties.ADDRESS}</p>
<p className={s.address}>
{selectedDealer.properties.CITY}, {selectedDealer.properties.STATE}{' '}
{selectedDealer.properties.ZIP_CODE}
</p>
</div>
<Button>Set Location</Button>
</div>
</InfoWindow>
)}
</GoogleMap>
</div>
</>
);
};
export function Search({ panTo }) {
const {
ready,
value,
suggestions: { status, data },
setValue,
clearSuggestions,
} = usePlacesAutocomplete({
requestOptions: {
location: { lat: () => 38.8299359, lng: () => -121.3070356 },
radius: 200 * 1000,
},
});
return (
<>
<div className={s.search__input}>
<i className={s.search__icon}>
<SearchIcon />
</i>
<Combobox
onSelect={async (address) => {
setValue(address, false);
clearSuggestions();
try {
const results = await getGeocode({ address });
const { lat, lng } = await getLatLng(results[0]);
panTo({ lat, lng });
} catch (error) {
console.log('error');
}
}}
>
<ComboboxInput
value={value}
onChange={(e: any) => {
setValue(e.target.value);
}}
disabled={!ready}
placeholder="Enter your location"
/>
<ComboboxPopover>
<ComboboxList>
{status === 'OK' &&
data.map(({ id, description }) => <ComboboxOption key={id} value={description} />)}
</ComboboxList>
</ComboboxPopover>
</Combobox>
</div>
</>
);
}
这是我的英雄组件:
英雄.tsx
import React from 'react';
import s from './Hero.scss';
import { RichText } from 'prismic-reactjs';
import { linkResolver } from 'utils/linkResolver';
import htmlSerializer from 'utils/htmlSerializer';
import { Search } from '../map/_Map';
export const Hero = ({ content, panTo }: any) => (
<div className={s.hero} data-theme={content.theme}>
<div className={s.hero__container}>
<div className={s.content}>
<h1 className={s.content__heading}>{RichText.asText(content.page_title)}</h1>
<div className={s.content__copy}>
{RichText.render(content.copy, linkResolver, htmlSerializer)}
</div>
<Search panTo={panTo}></Search>
</div>
</div>
</div>
);
从本质上讲,我需要在Hero
组件中使用Search
函数,但当我导出它并将其导入Hero
时,它的渲染效果很好,但它从未加载use-places-autocomplete
库,因此无法工作。我做错了什么?是否有任何方法可以导出搜索功能以重复使用?
我已经创建了一个SSCCE,正如这里的评论中所问的那样。如果我直接在Map组件中使用搜索功能,它就会起作用,但如果它被导入到英雄中,它就不会加载。
https://codesandbox.io/s/dawn-glitter-zi7lx
谢谢。
感谢您在codesandbox中提供代码的ssce。看起来您只是在<Map/>
中加载Google Maps Javascript脚本标记,而不是在<Hero/>
中加载。您可以在控制台日志中看到有一条消息要加载库。
为了使它发挥作用,我在索引.tsx中使用了@react google maps/api的LoadScript,并将两者和组件都放在一起,以便两个脚本都能工作。这是一个index.tsx.的样本
import React from "react";
import ReactDOM from "react-dom";
import { LoadScript } from "@react-google-maps/api";
import { Map } from "./Map";
import { Hero } from "./Hero";
const rootElement = document.getElementById("root");
const App = () => {
return (
<React.StrictMode>
<LoadScript
googleMapsApiKey="YOUR_API_KEY"
libraries={["places"]}
>
<Hero />
<Map />
</LoadScript>
</React.StrictMode>
);
};
ReactDOM.render(<App />, rootElement);
我还删除了Map.tsx中的useLoadScript,以确保它不会与index.tsx.中加载的脚本发生冲突