ReactJS и Google Maps - отображение маркеров - PullRequest
1 голос
/ 29 марта 2020

Я React newb ie, поэтому, возможно, задаю глупый вопрос, но это меня озадачило. В рамках своего обучения я создаю трехкомпонентное приложение - родитель About и двое детей (GoogleMap и MapMarkerDetails). Родитель выполняет координацию данных, и один дочерний элемент отображает карту Google с двумя маркерами по умолчанию, другой дочерний элемент отображает сведения о маркерах при нажатии.

Я добавляю функциональность для добавления нового маркера, когда карта нажата. Большая часть функционала работает - карты нарисованы, маркеры по умолчанию добавлены, и когда один из маркеров щелкается, это вызывает функцию родительского класса, которая обновляет его состояние, и это распространяется на элемент MapMarkerDetails и отображается простое сообщение.

Вот родительский класс, который я прокомментировал, чтобы помочь понять:

import React, { Component } from 'react';
import GoogleMap from './GoogleMap'
import MapMarkerDetails from './MapMarkerDetails'

class About extends Component {

state = {
    markers: [{
        position: { lat: 51.438759, lng: -2.864514 },
        label: 'A',
        map: null,
    }, {
        position: { lat: 51.433636, lng: -2.868734 },
        label: 'B',
        map: null,
    }],
    greeting: 'HelloA'
}

showMarkerInfo = (label) => {
    this.setState({greeting: ('Hello ' + label)})
}

/* 
Adding a new Marker 
This function is called from the child element GoogleMap
However, the setState(...) dosn't seem to propogate down to the GoogleMap element.
*/
addMarker = (latlng) => {
    let newMarker = [{
        position: { lat: latlng.lat(), lng: latlng.lng() },
        label: 'New',
        map: null,
    }];

    /* Creates a new array for updating state. Is this the best way to do this */
    let markers = [...this.state.markers, ...newMarker] 
    this.setState({markers});

    console.log(this.state) // This shows the added marker
}

render() {
    return (
        <div className="container">
            <h4 className="center">About</h4>
            <MapMarkerDetails details={this.state.greeting}/>
            <GoogleMap markers={this.state.markers} clickedMarker={this.showMarkerInfo} addMarker={this.addMarker}/>
        </div>
    )
    }
}

export default About;

Вот класс, который отображает Google Map и маркеры:

import React, { Component } from 'react';

class GoogleMap extends Component {
  constructor(props) {
    super(props);
    this.googleMapRef = React.createRef(); // Create a referance for Google Map to draw to
    console.log('Constructore')
  }

  componentDidMount(){
    console.log('componentDidMount')
    /* Create the Map */
    let googleMap = new window.google.maps.Map(this.googleMapRef.current, {
      zoom: 15,
      center: {
        lat: 51.436411,
        lng: -2.861980,
      },
      disableDefaultUI: true,
    })

    this.placeMMarkers(googleMap) // Place the markers
    this.addMapListner(googleMap) // Define a click listener to place new markers
  }

  /* Place the markers */
  placeMMarkers = (googleMap) => {
    this.props.markers.forEach((m) => {
      m.map = googleMap;
       let marker= new window.google.maps.Marker(m)
       marker.addListener('click', () => { this.props.clickedMarker(m.label); });
     }
   );
 }

 /* Map listeners */
  addMapListner = (googleMap) => {
    googleMap.addListener('click', (e) => {
      this.props.addMarker(e.latLng)
    })
  }

  render() {
    console.log('render: ' + this.props.markers) // This is showing the added markers
    return (
      <div
        id="google-map"
        ref={this.googleMapRef}
        style={{ width: '800px', height: '400px', float: 'left' }}>
      </div>
    )
  }
}

export default GoogleMap

Я добавил запись консоли в каждую функцию, чтобы я мог следить за происходящим.

Вот MapMarkerDetails, который отображает простое сообщение при нажатии на маркер. Все это прекрасно работает.

import React, { Component } from 'react';

class MapMarkerDetails extends Component {

    render(){
        return (
            <div style={{width: '100px', height: '400px', backgroundColor: 'gray', float: 'left'}}>
                {this.props.details}
            </div>
        )
    }
}

export default MapMarkerDetails

Описание проблемы

Когда пользователь нажимает на карту (не маркер), это вызывает функцию addMarker, которая передается от родительского класса About (фрагмент ниже). В функции addMarker, равной About, вводится значение широты / долготы. Это показывает, где пользователь щелкнул. Он преобразуется в объект данных marker, затем создается новый массив, который содержит маркеры по умолчанию и новый. Я не уверен, что создание моего нового массива выполнено наилучшим образом - если нет, дайте мне знать.

После создания нового массива мы обновляем состояние компонентов с помощью this.setState({markers}). Я думал, что это приведет к повторному render() и перерисовке карты с добавленным маркером. Но нет.

addMarker = (latlng) => {
    let newMarker = [{
        position: { lat: latlng.lat(), lng: latlng.lng() },
        label: 'New',
        map: null,
    }];

    /* Creates a new array for updating state. Is this the best way to do this */
    let markers = [...this.state.markers, ...newMarker] 
    this.setState({markers});

    console.log(this.state) // This shows the added marker
}

Что-то происходит, что приводит к вызову render() функции GoogleMap, но отображаются только оригинальные маркеры. Данные передаются в компонент GoogleMap, потому что я вижу вывод console.log('render: ' + this.props.markers). Но как мне получить ВСЕ маркеры для загрузки?

Посоветуйте, пожалуйста, как лучше всего About передать данные в GoogleMap, чтобы их можно было добавить в новый маркер.

1 Ответ

1 голос
/ 29 марта 2020

Так же, как вы используете componentDidMount для обязательного добавления маркеров при первой загрузке карты, вы должны использовать componentDidUpdate, чтобы сделать то же самое при смене реквизита. В вашем компоненте GoogleMap:

componentDidUpdate() {
  this.placeMMarkers()
}

Вместо того, чтобы передавать googleMap в качестве аргумента, я бы установил его как переменную экземпляра в componentDidMount:

this.googleMap = new window.google.maps.Map(...

, а затем изменил placeMMarkers использовать this.googleMap:

placeMMarkers = () => {
  this.props.markers.forEach((m) => {
    m.map = this.googleMap;
    // ...

Поскольку вы присоединяете обработчик событий в placeMMarkers, вы должны также добавить некоторые логи c для различия guish между новыми маркерами и существующими , чтобы избежать добавления нескольких обработчиков событий к существующим маркерам.

В ответ на ваш вопрос о том, как лучше установить состояние, я думаю, что у вас все в порядке, но вам не нужно ставить новый маркер внутри массива:

let newMarker = {
    position: { lat: latlng.lat(), lng: latlng.lng() },
    label: 'New',
    map: null,
};
let markers = [...this.state.markers, newMarker]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...