我想全力以赴在React中实现外部API,并希望能够使用Google Maps的API在子组件中显示地图.理想情况下,在使用诸如Axios之类的东西之前,我想了解如何在没有任何外部库的情况下进行此操作以对过程进行基本了解.
我的问题是:如何在React中使用Google文档中用于API的以下代码段?
<script async defer
src='https://maps.googleapis.com/maps/api/js?key=AIzaSyDZfVO29Iytspv4xz7S68doIoiztiRLhbk&callback=initMap'>
</script>
我尝试在index.html文件中使用它,但是当我在React的子组件中引用google对象时,出现错误:
./src/Main.js Line 114: ‘google’ is not defined no-undef
即使这不是首选或最优雅的方法,也将不胜感激,他们对如何在没有任何外部库的情况下实现API有了一些基本了解.谢谢!
编辑:
我的App.js:
import React, { Component } from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import Main from './Main';
import logo from './logo.svg';
import './App.css';
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();
class App extends Component {
render() {
return (
<MuiThemeProvider>
<Main />
</MuiThemeProvider>
);
}
}
export default App;
我的Main.js:
import React, { Component } from 'react';
import { FlatButton, Dialog, Card, Drawer, Paper, AppBar, Popover, Menu, MenuItem } from 'material-ui';
var items = [
{
id: 0,
name: 'Test 1',
city: 'Toronto',
longitude: 24.42142422,
latitude: 49.24121415,
tags: ['vegan', 'cheap', 'low-calorie'],
reviews: [
{
rating: 5,
reviewText: 'This was an amazing restaurant. Incredibly fast service, a large variety of options, and delicIoUs food. I\'ll be here often',
author: 'Mohammad Sheikh',
date: new Date(),
helpfulCount: 5,
notHelpfulCount: 4
},
{
rating: 2,
reviewText: 'Absolutely horrible. Please stop making food.',
author: 'dissatisfied Customer',
date: new Date(),
helpCount: 2,
notHelpfulCount: 3
},
],
foods:
[
{
id: 0,
name: 'Salad',
img: 'http://www.images.google.com/',
tags: ['vegan', 'low-calorie', 'cheap'],
nutrition:
{
calories: 300,
fat: 5,
carbs: 40,
protein: 24
},
reviews:
{
rating: 4,
reviewText: 'Decent salad. Would recommend.',
author: 'Vegan bro',
date: new Date(),
helpCount: 4,
notHelpfulCount: 1
}
},
{
id: 1,
name: 'Pasta',
img: 'http://www.images.google.com/',
tags: ['vegetarian', 'dinner'],
nutrition:
{
calories: 800,
fat: 40,
carbs: 80,
protein: 20
},
reviews:
{
rating: 5,
reviewText: 'Absolutely amazing',
author: 'Food Fan',
date: new Date(),
helpCount: 8,
notHelpfulCount: 4
}
},
],
},
];
const paperStyle = {
height: 100,
width: 100,
margin: 20,
textAlign: 'center',
display: 'table',
position: 'relative',
clear: 'both',
float: 'right',
zIndex: 6
};
const paperContent = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
}
class RestaurantDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false
}
}
render() {
return (
<Dialog>
</Dialog>
)
}
}
class RestaurantButton extends React.Component {
constructor(props) {
super(props);
}
handleClick = () => {
}
render() {
return (
<FlatButton style={{width: '100%', height: '64px'}} onClick>
{this.props.item.city}
<RestaurantDialog restaurant={this.props.item.name} />
</FlatButton>
)
}
}
class MapComponent extends React.Component {
constructor(props) {
super(props);
this.googleChecker = this.googleChecker.bind(this);
this.renderMap = this.renderMap.bind(this);
}
googleChecker() {
if (!window.google.maps) {
setTimeout(this.googleChecker, 100);
}
else {
this.renderMap();
}
}
renderMap() {
var map = google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: {lat: 0, lng: 0}
});
}
componentDidMount() {
this.googleChecker();
}
render() {
const selections = this.props.currentSelections;
const buttons = items.filter((item) => {
for (let i = 0; i < selections.length; i++) {
if (selections.map((selection) => {return selection.toLowerCase()}).indexOf(item.tags[i].toLowerCase()) > -1) {
return true;
}
}}).map((item) => {
return (
<RestaurantButton style={{zIndex: '5'}} item={item} />
)
});
return (
<Paper id='map' zDepth={3} style={{height: '300px', width: '100%', backgroundColor: 'white', position: 'absolute'}}>
{ buttons }
</Paper>
)
}
}
class SelectionIcon extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Paper circle={true} zDepth={5} style={this.props.style} key={this.props.index} onClick={this.props.close} >
<div style={paperContent}>{this.props.item}</div>
</Paper>
)
}
}
class SelectionIcons extends React.Component {
constructor(props) {
super(props);
}
handleSelectionClose = (e) => {
e.currentTarget.open = false;
}
render() {
let currentSelections = this.props.currentSelections.slice();
let list = currentSelections.map((item, i) => {
return (
<Paper circle={true} zDepth={5} style={paperStyle} key={i} onClick={this.handleSelectionClose}>
<div style={paperContent}>{item}</div>
</Paper>
)
});
return (
<div>
{list}
</div>
)
}
}
class Main extends React.Component {
constructor(props){
super(props);
this.state = {
navMenuOpen: false,
currentSelections: []
}
}
handleMenuButtonTouch = (e) => {
this.setState({
anchorEl: e.currentTarget.parentNode,
navMenuOpen: !this.state.navMenuOpen
})
}
handleRequestChange = (change) => {
this.setState({
navMenuOpen: change.open
})
console.log(document.getElementById('test').style);
}
handleMenuClick = (e) => {
let currentSelections = this.state.currentSelections.slice();
if (currentSelections.indexOf(e) > -1) {
currentSelections.splice(currentSelections.indexOf(e), 1);
}
else {
currentSelections.push(e);
}
console.log(currentSelections)
this.setState({ currentSelections });
}
render() {
return (
<div>
<AppBar title='The App' id='test' zDepth={1} onLeftIconButtonTouchTap={this.handleMenuButtonTouch} style={{zIndex: 4}}>
</AppBar>
<Drawer
id='test2'
open={this.state.navMenuOpen}
onRequestChange={() => {this.handleRequestChange;}}
containerStyle={{zIndex: 3, marginTop: '64px'}}>
<Menu>
<MenuItem primaryText='High Protein' onClick={() => this.handleMenuClick('High Protein')} />
<MenuItem primaryText='vegetarian' onClick={() => this.handleMenuClick('vegetarian')} />
<MenuItem primaryText='Vegan' onClick={() => this.handleMenuClick('Vegan')} />
</Menu>
</Drawer>
<MapComponent items={items} currentSelections={this.state.currentSelections} />
<SelectionIcons currentSelections={this.state.currentSelections} />
</div>
)
}
}
export default Main;
我的index.html:
<!doctype html>
<html lang="en">
<head>
<Meta charset="utf-8">
<Meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<Meta name="theme-color" content="#000000">
<!--
manifest.json provides Metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<script async defer
src='https://maps.googleapis.com/maps/api/js?key=AIzaSyDZfVO29Iytspv4xz7S68doIoiztiRLhbk'>
</script>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, Meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
解决方法:
该问题与使用Google Maps API时异步和延迟的工作方式有关.
基本上,当您的代码到达必须渲染地图的位置时,尚未加载Google Api.请看一下这篇文章,以了解其工作原理:
https://stackoverflow.com/a/36909530/2456879
有两种解决方案.
解决方案一
不要在脚本标签中使用异步和延迟,以便允许在执行应用之前下载脚本:
<script src='https://maps.googleapis.com/maps/api/js?key=AIzaSyDZfVO29Iytspv4xz7S68doIoiztiRLhbk&callback=initMap'>
</script>
解决方案二
创建某种递归检查器,以查看是否加载了Google Api,以便在google maps api可用后继续执行您的应用程序:
class MyMap extends Component{
constructor(props){
super(props);
this.googleChecker = this.googleChecker.bind(this);
this.renderMap = this.renderMap.bind(this);
}
googleChecker() {
// check for maps in case you're using other Google Api
if(!window.google.maps) {
setTimeout(googleChecker, 100);
console.log("not there yet");
} else {
console.log("we're good to go!!");
// the google maps api is ready to use, render the map
this.renderMap();
}
}
renderMap(){
const coords = { lat: 41.375885, lng: 2.177813 };
// create map instance
new google.maps.Map(this.refs.mapContainer, {
zoom: 16,
center: {
lat: coords.lat,
lng: coords.lng
}
});
}
componentDidMount(){
this.googleChecker();
}
render(){
return(
<div className="card map-holder">
<div className="card-block" ref="mapContainer" />
</div>
);
}
}
您也可以使用一个promise并通过checker方法或类似的方法来解决它.您也可以将该代码放在父组件中,在状态中存储一个布尔值,然后将其传递给子组件,以便在api可用后开始呈现地图.此方法也可以与redux和redux thunk一起使用,以解决承诺.如您所见,根据您的方法,有几种选择.
这是使用超时检查器的实时示例:
https://jsbin.com/tejutihoka/edit?js,output
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。