Я 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
, чтобы их можно было добавить в новый маркер.