React:导出函数以在另一个组件中使用
React: Export function to use in another component
我正在我的 Gatsby 网站上构建一个 Google 地图,其中包含一个搜索框,允许用户搜索他们的位置。我有一个 Hero
组件和一个 Map
组件,所有功能都内置在 Map
组件中; Google 地图 API、自动完成、Google 地点等。这是现在的样子:
Map.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>
</>
);
}
这是我的英雄组件:
Hero.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 组件中使用它,搜索功能可以使用,但是如果将它导入到 hero 中,它不会加载。
https://codesandbox.io/s/dawn-glitter-zi7lx
谢谢。
感谢您在 codesandbox 中提供代码的 sscce。您似乎只在 <Map/>
内而不是 <Hero/>
内加载 Google 地图 Javascript 脚本标签。您可以在您的控制台日志中看到一条加载库的消息。
为了让它发挥作用,我在你的 index.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 中加载的脚本发生冲突。
我正在我的 Gatsby 网站上构建一个 Google 地图,其中包含一个搜索框,允许用户搜索他们的位置。我有一个 Hero
组件和一个 Map
组件,所有功能都内置在 Map
组件中; Google 地图 API、自动完成、Google 地点等。这是现在的样子:
Map.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>
</>
);
}
这是我的英雄组件:
Hero.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 组件中使用它,搜索功能可以使用,但是如果将它导入到 hero 中,它不会加载。
https://codesandbox.io/s/dawn-glitter-zi7lx
谢谢。
感谢您在 codesandbox 中提供代码的 sscce。您似乎只在 <Map/>
内而不是 <Hero/>
内加载 Google 地图 Javascript 脚本标签。您可以在您的控制台日志中看到一条加载库的消息。
为了让它发挥作用,我在你的 index.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 中加载的脚本发生冲突。