2017-04-26 8 views
2

私はthisチュートリアルの次に、ライブラリを怠惰に読み込むGoogle Maps React Componentを記述しています。私はthisのScriptCacheを実装しました。私が持っている問題は、コードはLoading map...しか表示されず、マップは決してレンダリングされないということです。私が逃したどんな明白なことか?GoogleマップライブラリをTypeScriptに反応させて読み込みません

のindex.html:

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="utf-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css"> 
    <title>App</title> 
</head> 
<body> 
<div id="app"></div> 
<script src="/Scripts/dist/bundle.js"></script> 
</body> 
</html> 

index.tsx:

import * as React from "react"; 
import * as ReactDOM from "react-dom"; 
import * as ReactRouter from "react-router"; 
import * as ReactBootstrap from "react-bootstrap"; 
import Container from "./Google/GoogleMapComponent"; 

ReactDOM.render(
    <div> 
     <Container google={(window as any).google} /> 
    </div>, 
    document.getElementById("app") 
); 

GoogleApi.tsx:

export const GoogleApi = function (opts: any) { 
    opts = opts || {} 

    const apiKey: any = opts.apiKey; 
    const libraries: any = opts.libraries || []; 
    const client: any = opts.client; 
    const URL: string = 'https://maps.googleapis.com/maps/api/js'; 

    const googleVersion: string = '3.22'; 
    let script: any = null; 
    let google: any = (window as any).google = null; 
    let loading = false; 
    let channel: any = null; 
    let language: any = null; 
    let region: any = null; 

    let onLoadEvents: any[] = []; 

    const url =() => { 
     let url = URL; 
     let params = { 
      key: apiKey, 
      callback: 'CALLBACK_NAME', 
      libraries: libraries.join(','), 
      client: client, 
      v: googleVersion, 
      channel: channel, 
      language: language, 
      region: region 
     } 

     let paramStr = Object.keys(params) 
      .filter(k => !!(params as any)[k]) 
      .map(k => `${k}=${(params as any)[k]}`).join('&'); 

     return `${url}?${paramStr}`; 
    } 

    return url(); 
} 

export default GoogleApi 

GoogleApiComponent.tsx:

import * as React from "react"; 
import * as ReactDOM from 'react-dom' 

import cache from './ScriptCache' 
import GoogleApi from './GoogleApi' 

const defaultMapConfig = {} 
export const wrapper = (options: any) => (WrappedComponent: any) => { 
    const apiKey = options.apiKey; 
    const libraries = options.libraries || ['places']; 

    class Wrapper extends React.Component<any, any> { 
     constructor(props: any, context: any) { 
      super(props, context); 

      this.state = { 
       loaded: false, 
       map: null, 
       google: null 
      } 
     } 

     scriptCache: any; 
     map: any; 
     mapComponent: any 
     refs: { 
      [string: string]: any; 
      map: any; 
     } 

     componentDidMount() { 
      const refs: any = this.refs; 
      this.scriptCache.google.onLoad((err: any, tag: any) => { 
       const maps = (window as any).google.maps; 
       const props = Object.assign({}, this.props, { 
        loaded: this.state.loaded 
       }); 

       const mapRef: any = refs.map; 

       const node = ReactDOM.findDOMNode(mapRef); 
       let center = new maps.LatLng(this.props.lat, this.props.lng) 

       let mapConfig = Object.assign({}, defaultMapConfig, { 
        center, zoom: this.props.zoom 
       }) 

       this.map = new maps.Map(node, mapConfig); 

       this.setState({ 
        loaded: true, 
        map: this.map, 
        google: (window as any).google 
       }) 
      }); 
     } 

     componentWillMount() { 
      this.scriptCache = cache({ 
       google: GoogleApi({ 
        apiKey: apiKey, 
        libraries: libraries 
       }) 
      }); 
     } 

     render() { 
      const props = Object.assign({}, this.props, { 
       loaded: this.state.loaded, 
       map: this.state.map, 
       google: this.state.google, 
       mapComponent: this.refs.map 
      }) 
      return (
       <div> 
        <WrappedComponent {...props} /> 
        <div ref='map' /> 
       </div> 
      ) 
     } 

    } 

    return Wrapper; 
} 

export default wrapper; 

GoogleMapComponent.tsx:

import * as React from "react"; 
import * as ReactDOM from 'react-dom' 

import GoogleApiComponent from "./GoogleApiComponent"; 

export class Container extends React.Component<any, any> { 
    render() { 
    const style = { 
     width: '100px', 
     height: '100px' 
    } 
    return (
     <div style={style}> 
      <Map google={this.props.google} /> 
     </div> 
    ) 
    } 
} 

export default GoogleApiComponent({ 
    apiKey: 'AIzaSyAyesbQMyKVVbBgKVi2g6VX7mop2z96jBo ' //From Fullstackreact.com 
})(Container) 

export class Map extends React.Component<any, any> { 

    refs: { 
     [string: string]: any; 
     map: any; 
    } 
    map: any; 

    componentDidMount() { 
     this.loadMap(); 
    } 

    componentDidUpdate(prevProps: any, prevState: any) { 
     if (prevProps.google !== this.props.google) { 
      this.loadMap(); 
     } 
    } 

    loadMap() { 
     if (this.props && this.props.google) { 
      // google is available 
      const {google} = this.props; 
      const maps = google.maps; 

      const mapRef = this.refs.map; 
      const node = ReactDOM.findDOMNode(mapRef); 

      let zoom = 14; 
      let lat = 37.774929; 
      let lng = -122.419416; 
      const center = new maps.LatLng(lat, lng); 
      const mapConfig = Object.assign({}, { 
       center: center, 
       zoom: zoom 
      }) 
      this.map = new maps.Map(node, mapConfig); 
     } 
     // ... 
    } 

    render() { 
     return (
      <div ref='map'> 
       Loading map... 
     </div> 
     ) 
    } 
} 

ScriptCache.tsx:

let counter = 0; 
let scriptMap = new Map(); 

export const ScriptCache = (function (global: any) { 
    return function ScriptCache(scripts: any) { 
     const Cache: any = {} 

     Cache._onLoad = function (key: any) { 
      return (cb: any) => { 
       let stored = scriptMap.get(key); 
       if (stored) { 
        stored.promise.then(() => { 
         stored.error ? cb(stored.error) : cb(null, stored) 
        }) 
       } else { 
        // TODO: 
       } 
      } 
     } 

     Cache._scriptTag = (key: any, src: any) => { 
      if (!scriptMap.has(key)) { 
       let tag : any = document.createElement('script'); 
       let promise = new Promise((resolve: any, reject: any) => { 
        let resolved = false, 
         errored = false, 
         body = document.getElementsByTagName('body')[0]; 

        tag.type = 'text/javascript'; 
        tag.async = false; // Load in order 

        const cbName = `loaderCB${counter++}${Date.now()}`; 
        let cb: any; 

        let handleResult = (state: any) => { 
         return (evt: any) => { 
          let stored = scriptMap.get(key); 
          if (state === 'loaded') { 
           stored.resolved = true; 
           resolve(src); 
           // stored.handlers.forEach(h => h.call(null, stored)) 
           // stored.handlers = [] 
          } else if (state === 'error') { 
           stored.errored = true; 
           // stored.handlers.forEach(h => h.call(null, stored)) 
           // stored.handlers = []; 
           reject(evt) 
          } 

          cleanup(); 
         } 
        } 

        const cleanup =() => { 
         if (global[cbName] && typeof global[cbName] === 'function') { 
          global[cbName] = null; 
         } 
        } 

        tag.onload = handleResult('loaded'); 
        tag.onerror = handleResult('error') 
        tag.onreadystatechange =() => { 
         handleResult(tag.readyState) 
        } 

        // Pick off callback, if there is one 
        if (src.match(/callback=CALLBACK_NAME/)) { 
         src = src.replace(/(callback=)[^\&]+/, `$1${cbName}`) 
         cb = (window as any)[cbName] = tag.onload; 
        } else { 
         tag.addEventListener('load', tag.onload) 
        } 
        tag.addEventListener('error', tag.onerror); 

        tag.src = src; 
        body.appendChild(tag); 
        return tag; 
       }); 
       let initialState = { 
        loaded: false, 
        error: false, 
        promise: promise, 
        tag 
       } 
       scriptMap.set(key, initialState); 
      } 
      return scriptMap.get(key); 
     } 

     Object.keys(scripts).forEach(function (key) { 
      const script = scripts[key]; 
      Cache[key] = { 
       tag: Cache._scriptTag(key, script), 
       onLoad: Cache._onLoad(key) 
      } 
     }) 

     return Cache; 
    } 
})(window) 

export default ScriptCache; 
+0

この記事では私の答えをチェックアウト:http://stackoverflow.com/a/41710341/1550476 –

+0

@NguyenThanhおかげで、それはexperimentalDecoratorsを使用し、また、私は '警告だ:メインリアクトパッケージを経由してアクセスするPropTypesを推奨されません。私のブラウザでは、代わりにnpmのprop-typesパッケージを使用してください。現在のコードで動作しています。 – Ogglas

答えて

1

コンテナが印刷されます。この何のように見えるにもかかわらず:

export class Container extends React.Component<any, any> { 
    render() 
    { 
     const style = { 
      width: '100px', 
      height: '100px' 
     } 
     return (
      <div style={style}> 
       <Map google={this.props.google} /> 
      </div> 
     ) 
    } 
} 

私はしかし、幅と高さを設定しない場合地図上に直接置くと正しく印刷されます。幅と高さをパーセントで追加することはできません。

export class Map extends React.Component<any, any> { 
... 
    render() { 
     const style = { 
      width: '100vw', 
      height: '100vh' 
     } 
     return (
      <div ref='map' style={style}> 
       Loading map... 
     </div> 
     ) 
    } 
} 
関連する問題