【问题标题】:Load Google Place API in Gatsbyjs (Reactjs) project在 Gatsbyjs (Reactjs) 项目中加载 Google Place API
【发布时间】:2018-10-20 19:39:28
【问题描述】:

我正在尝试使用来自 Google Place API 的自动完成地址服务。

找到这个库: https://github.com/kenny-hibino/react-places-autocomplete#load-google-library

它要求在我的项目中加载库: https://github.com/kenny-hibino/react-places-autocomplete#getting-started

如果是纯 Reactjs 项目,我会在 public/index.html 中进行。但是 Gatsbyjs 项目中的 public/index.html 每次运行时都会被删除并重新生成:

Gatsby develop

命令行。

如何在我的 Gatsbyjs 项目中使用 Google Place API?

更新

我尝试了两种方法来实现这一点。

  1. 在 /layouts/index.js 中使用 React-Helmet,如下所示:

        <Helmet>
          <script src="https://maps.googleapis.com/maps/api/js?key={API}&libraries=places&callback=initAutocomplete" async defer></script>
        </Helmet>
    

  2. 将脚本引用放在/public/index.html中,如下所示:

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charSet="utf-8" />
        <meta http-equiv="x-ua-compatible" content="ie=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <title data-react-helmet="true"></title>
        <script src="/socket.io/socket.io.js"></script>
        <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key={API_KEY}&libraries=places" async defer ></script>
    </head>
    
    <body>
        <div id="___gatsby"></div>
        <script src="/commons.js"></script>
    </body>
    
    </html>
    

对于第一种解决方案,每次刷新页面后,项目都会抛出一个错误,要求加载 Google JavaScript Map API。

对于第二种解决方案,每次我通过命令行重新启动 Gatsby 后:gatsby develop

它会重新生成 index.html,它会清除我在其中的 JavaScript 引用。

【问题讨论】:

  • key=API 这是什么?
  • @MrUpsidown 这只是一个占位符,不显示我的真实 api 密钥。
  • 为什么要投反对票?快乐学习
  • 请提供Minimal, Complete, and Verifiable example,以便重现问题并分享您在控制台中看到的确切错误。
  • @MrUpsidown 当然会更新问题。但不会包含真正的 API 密钥 :)

标签: reactjs google-maps-api-3 google-places-api gatsby


【解决方案1】:

您不应使用 GatsbyJS 修改 public forlder 中的任何文件。

相反,我建议你customize your html.js file

为此,首先运行:

cp .cache/default-html.js src/html.js

您应该在 /src/html.js 中有 html.js 文件。

现在您可以将&lt;script&gt; 标签放在&lt;head&gt; 中。

【讨论】:

  • 没错,我就是这样做的,它解决了我的问题~!谢谢
  • @Franva 我仍然无法让它工作。您是否还做了这里没有提到的其他事情?
  • 我发现我需要使用dangerouslySetInnerHTML,但后来遇到了其他问题,所以最后我只使用了google-maps-react 包。谢谢你。
【解决方案2】:

2020 年 2 月 24 日更新

这是一个更现代的实现,它使用 React hooks 并基于 React.memo 和自定义 shouldUpdate 函数进行了一些性能优化。详情请见this blog post

import { functions, isEqual, omit } from 'lodash'
import React, { useState, useEffect, useRef } from 'react'

function Map({ options, onMount, className, onMountProps }) {
  const ref = useRef()
  const [map, setMap] = useState()

  useEffect(() => {
    // The Map constructor modifies its options object in place by adding
    // a mapTypeId with default value 'roadmap'. This confuses shouldNotUpdate.
    // { ...options } prevents this by passing in a copy.
    const onLoad = () =>
      setMap(new window.google.maps.Map(ref.current, { ...options }))
    if (!window.google) {
      const script = document.createElement(`script`)
      script.src = `https://maps.googleapis.com/maps/api/js?key=` + YOUR_API_KEY
      document.head.append(script)
      script.addEventListener(`load`, onLoad)
      return () => script.removeEventListener(`load`, onLoad)
    } else onLoad()
  }, [options])

  if (map && typeof onMount === `function`) onMount(map, onMountProps)

  return (
    <div
      style={{ height: `60vh`, margin: ` 1em 0`, borderRadius: ` 0.5em` }}
      {...{ ref, className }}
    />
  )
}

function shouldNotUpdate(props, nextProps) {
  const [funcs, nextFuncs] = [functions(props), functions(nextProps)]
  const noPropChange = isEqual(omit(props, funcs), omit(nextProps, nextFuncs))
  const noFuncChange =
    funcs.length === nextFuncs.length &&
    funcs.every(fn => props[fn].toString() === nextProps[fn].toString())
  return noPropChange && noFuncChange
}

export default React.memo(Map, shouldNotUpdate)

Map.defaultProps = {
  options: {
    center: { lat: 48, lng: 8 },
    zoom: 5,
  },
}

旧答案

使用 html.js

像这样修改src/html.js(正如 Nenu 建议的那样)是一种选择。

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class HTML extends Component {
  render() {
    return (
      <html {...this.props.htmlAttributes}>
        <head>
          <meta charSet="utf-8" />
          <meta httpEquiv="x-ua-compatible" content="ie=edge" />
          <meta
            name="viewport"
            content="width=device-width, initial-scale=1, shrink-to-fit=no"
          />
          {this.props.headComponents}
        </head>
        <body {...this.props.bodyAttributes}>
          {this.props.preBodyComponents}
          <div
            key={`body`}
            id="___gatsby"
            dangerouslySetInnerHTML={{ __html: this.props.body }}
          />
          {this.props.postBodyComponents}
          // MODIFICATION // ===================
          <script
            src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"
            async
            defer
          />
          // ===================
        </body>
      </html>
    )
  }
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}

然后,您可以通过window.google.maps.(Map|Marker|etc.) 在您项目中的任何位置访问 Google Maps API。

React 方式

不过,对我来说,这有点不合时宜。如果你想要一个可重用的 React 组件,你可以将它作为 import Map from './Map' 导入到任何页面或模板中,我建议这样做。 (提示:等效功能组件见下文更新。)

// src/components/Map.js
import React, { Component } from 'react'

export default class Map extends Component {
  onLoad = () => {
    const map = new window.google.maps.Map(
      document.getElementById(this.props.id),
      this.props.options
    )
    this.props.onMount(map)
  }

  componentDidMount() {
    if (!window.google) {
      const script = document.createElement('script')
      script.type = 'text/javascript'
      script.src = `https://maps.google.com/maps/api/js?key=YOUR_API_KEY`
      const headScript = document.getElementsByTagName('script')[0]
      headScript.parentNode.insertBefore(script, headScript)
      script.addEventListener('load', () => {
        this.onLoad()
      })
    } else {
      this.onLoad()
    }
  }

  render() {
    return <div style={{ height: `50vh` }} id={this.props.id} />
  }
}

像这样使用它:

// src/pages/contact.js
import React from 'react'

import Map from '../components/Map'

const center = { lat: 50, lng: 10 }
const mapProps = {
  options: {
    center,
    zoom: 8,
  },
  onMount: map => {
    new window.google.maps.Marker({
      position: center,
      map,
      title: 'Europe',
    })
  },
}

export default function Contact() {
  return (
    <>
      <h1>Contact</h1>
      <Map id="contactMap" {...mapProps} />
    </>
  )
}

【讨论】:

  • 我喜欢你的方法,以后再试试。感谢分享
【解决方案3】:

让我清醒的是在我的项目的根目录中创建一个 gatsby-ssr.js 文件,然后在其中包含脚本,如下所示:

import React from "react"

export function onRenderBody({ setHeadComponents }) {
  setHeadComponents([
    <script
      key="abc"
      type="text/javascript"
      src={`https://maps.googleapis.com/maps/api/js?key=${process.env.GATSBY_API_KEY}&libraries=places`}
    />,
  ])
}

不要忘记在 .env.development 和 .env.production 文件中包含 GATSBY_API_KEY 或任何您想调用的名称:

GATSBY_API_KEY=...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-15
    • 1970-01-01
    • 1970-01-01
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-24
    相关资源
    最近更新 更多