I've got App.js which has a state called layers and I want to update the layers state from layers class.
Below is my layers class which doesn't return anything.
What is the best practise to do this in react js?
const layers = createReactClass({
getInitialState() {
return {
layers: [],
}
},
addTileLayer: function () {
esriPromise([ 'esri/layers/TileLayer']).then(([TileLayer]) => {
let tileLayer = new TileLayer(
{
id: 'XX',
opacity: '0.55',
visible: false
});
this.setState({
layers: [...this.state.layers, tileLayer]
})
}).catch((err) => console.error(err));
},
});
export default layers;
Please note I've added the app.js below. Right now addTileLayer function resides inside the app.js
let createReactClass = require('create-react-class');
const App = createReactClass({
getInitialState() {
return {
status: 'loading',
extent: {},
layers: [],
searchWidget: null,
map: null,
view: null,
}
this.handleFail = this.handleFail.bind(this);
},
componentWillMount(){
this.addTileLayer();
this.addFeatureLayer();
},
addTileLayer: function () {
esriPromise([ 'esri/layers/TileLayer']).then(([TileLayer]) => {
let tileLayer = new TileLayer(
{
id: 'xxx',
opacity: '0.55',
visible: false
});
this.setState({
layers: [...this.state.layers, tileLayer]
})
}).catch((err) => console.error(err));
},
render(){
const maxZoom = 15;
const minZoom = 4;
return(
<div id = 'container'>
<div id="searchWidget"></div>
<div id = 'main-content'>
<Map
class = 'full-screen-map'
mapProperties = {
{
basemap: 'dark-gray',
showLabels : true,
logo: false,
sliderPosition: 'bottom-left',
layers: this.state.layers,
}
}
viewProperties = {
{
extent: this.state.extent,
zoom: 12,
minZoom: minZoom,
maxZoom: maxZoom,
ui: {
components: [this.state.searchWidget, 'compass']
}
}
}
onFail={this.handleFail}
onLoad={this.handleMapLoad}
>
</Map>
</div>
</div>
)
}
I think a clean way to do this is to maybe have a callback function passed into layers.addTileLayer
layers.addTileLayer((layer) => this.setState({layer:layer}))
and your new layers class could be
const layers = createReactClass({
addTileLayer: function (updateLayers) {
esriPromise([ 'esri/layers/TileLayer']).then(([TileLayer]) => {
let tileLayer = new TileLayer(
{
id: 'XX',
opacity: '0.55',
visible: false
});
updateLayers(tileLayer) //this is new
}).catch((err) => console.error(err));
},
});
export default layers;
this way you don't need to keep track of the state in two places! and still get to encapsulate this functionality in another class :)
Related
here I get a bug when trying the leaflet-routing-machine lib.
The bug is that the "Waypoints" section renders 2 times.
why is it able to render 2 times? Can you guys help me? Thank you
My Code in below =>
My Code =
import { useEffect } from "react";
import L from "leaflet";
import "leaflet-routing-machine/dist/leaflet-routing-machine.css";
import "leaflet-routing-machine";
import { useMap } from "react-leaflet";
L.Marker.prototype.options.icon = L.icon({
iconUrl: "https://unpkg.com/leaflet#1.7.1/dist/images/marker-icon.png",
});
export default function Routing() {
const map = useMap();
const routingControl = L.Routing.control({
waypoints: [
L.latLng(-6.3094117, 106.8240261),
L.latLng(-6.2185648, 106.7996082),
],
lineOptions: {
styles: [{ color: "#6FA1EC", weight: 4 }],
},
routeWhileDragging: true,
draggableWaypoints: true,
fitSelectedRoutes: true,
}).addTo(map);
useEffect(() => {
return () => map.removeControl(routingControl);
}, [map, routingControl]);
function createButton(label, container) {
let btn = L.DomUtil.create("button", "", container);
btn.setAttribute("type", "button");
btn.innerHTML = label;
return btn;
}
map.on("click", function (e) {
let container = L.DomUtil.create("div"),
startBtn = createButton("Start from this location", container),
destBtn = createButton("Go to this location", container);
container.setAttribute("class", "leaflet-popup-btn-box");
L.DomEvent.on(startBtn, "click", function () {
routingControl.spliceWaypoints(0, 1, e.latlng);
map.closePopup();
});
L.DomEvent.on(destBtn, "click", function () {
routingControl.spliceWaypoints(
routingControl.getWaypoints().length - 1,
1,
e.latlng
);
map.closePopup();
});
L.popup().setContent(container).setLatLng(e.latlng).openOn(map);
});
return null;
}
I am trying to build a Vue component that takes in input an array of gps (lat/lng) data and which have to be drawn on a map using a google maps polyline. It is working until this point. The problem rises when I try to use map.fitbounds in order to manage the zoom and center out the map to that polyline. As per vue3-google-map documentation I tried to create a ref to the map object with ref="mapRef". Unfortunately in the mounted() hook it seems that I can'f find that object, inside Vue DevTools it will later show up.
<template>
<GoogleMap ref="mapRef" api-key="<myapikey>" style="width: 100%; height: 500px" :center="center" :zoom="15">
<Polyline :options="path" />
</GoogleMap>
</template>
<script>
import {
defineComponent,
ref
} from 'vue'
import {
GoogleMap,
Polyline
} from 'vue3-google-map'
export default defineComponent({
components: {
GoogleMap,
Polyline
},
setup() {
const mapRef = ref(null)
return {
mapRef
}
},
methods: {
managebounds(path) {
this.intervalid = setInterval(function () {
if (!this.bounds && !this.loaded) {
if (window.google && window.google.maps && path) {
this.bounds = new window.google.maps.LatLngBounds()
this.loaded = true
clearInterval(this.intervalid)
console.log("AFTER CLEAR")
this.bounds.extend(path.path[0]) //i take the first and last point of the polyline
this.bounds.extend(path.path[path.path.length - 1])
var extendBy = 0.001;
console.log("EXTEND1")
var point1 = new window.google.maps.LatLng(
this.bounds.getNorthEast().lat() + extendBy,
this.bounds.getNorthEast().lng() + extendBy
)
var point2 = new window.google.maps.LatLng(
this.bounds.getSouthWest().lat() - extendBy,
this.bounds.getSouthWest().lng() - extendBy
)
this.bounds.extend(point1);
console.log("EXTEND2")
this.bounds.extend(point2);
console.log("FITTING BOUNDS")
this.intervalid = setInterval(function () {
if (this.$refs.mapRef.value?.ready) {
console.log("LOADED")
this.$refs.mapRef.value.map.fitBounds(this.bounds); //here mapRef is undefined
clearInterval(this.intervalid)
} else {
console.log("NOT LOADED")
}
}, 1000)
} else {
console.log("OUT")
}
}
}, 500)
}
},
mounted() {
this.$nextTick(function () {
this.managebounds(this.path)
})
},
data() {
return {
path: {
path: this.gpspath.path,
strokeColor: this.gpspath.color
},
bounds: null,
loaded: false,
intervalid: -1
}
},
props: {
"gpspath": {
type: [],
default: [{}]
}
}
})
</script>
Any hint about fixing this issue?
I had to read again the Vue documentation about computed properties and figured out is was a bad idea trying to build a Method.
I made a function inside the setup() hook and called that from my computed data.
The working result is in the following code:
setup() {
const mapRef = ref(null)
function centermap(start, end){
if (mapRef.value?.ready) {
const gmap = mapRef.value.map;
const api = mapRef.value.api;
this.bounds = new api.LatLngBounds();
this.bounds.extend(start);
this.bounds.extend(end);
gmap.fitBounds(this.bounds);
}else{
console.log("NOT READY")
}
}
return {
mapRef, centermap
}
},
computed: {
path() {
if(this.gpspath){
let filteredpath = this.gpspath.path.filter(x=>Math.abs(x.lat)>1)
if(filteredpath && filteredpath.length>1)
this.centermap(filteredpath[0], filteredpath[filteredpath.length-1])
return {
path: filteredpath,
strokeColor: this.gpspath.color
}
}else{
return {
path: [],
strokeColor: "#ffffff"
}
}
}
}
I also faced this problem; here is my solution:
Page with map:
import { mapFitBounds } from "#/composables/mapFitBounds";
const mapRef = ref(null);
watch(
() => mapRef.value?.ready,
(ready) => {
if (!ready) return;
mapFitBounds(mapRef, markersArray);
}
);
composables/mapFitBounds.js:
export function mapFitBounds(mapRef, markers) {
let bounds;
const api = mapRef.value.api;
const map = mapRef.value.map;
bounds = new api.LatLngBounds();
for (let i = 0; i < markers.length; i++) {
bounds.extend(markers[i]);
}
map.fitBounds(bounds);
}
this is my react map component:
import 'mapbox-gl/dist/mapbox-gl.css';
import './switcher/switcher.css';
import mapboxgl from 'mapbox-gl';
import React, { useRef, useLayoutEffect, useEffect, useState } from 'react';
import { deviceCategories } from '../common/deviceCategories';
import { loadIcon, loadImage } from './mapUtil';
import { styleCarto} from './mapStyles';
import { useAttributePreference } from '../common/preferences';
const element = document.createElement('div');
element.style.width = '100%';
element.style.height = '100%';
export const map = new mapboxgl.Map({
container: element,
style: styleCarto(),
center: [80.379370, 23.846870],
zoom: 4.8
});
let ready = false;
const readyListeners = new Set();
const addReadyListener = listener => {
readyListeners.add(listener);
listener(ready);
};
const removeReadyListener = listener => {
readyListeners.delete(listener);
};
const updateReadyValue = value => {
ready = value;
readyListeners.forEach(listener => listener(value));
};
const initMap = async () => {
const background = await loadImage('images/background.svg');
await Promise.all(deviceCategories.map(async category => {
if (!map.hasImage(category)) {
const imageData = await loadIcon(category, background, `images/icon/car.png`);
map.addImage(category, imageData, { pixelRatio: window.devicePixelRatio });
}
}));
updateReadyValue(true);
};
map.on('load', initMap);
map.addControl(new mapboxgl.NavigationControl({
showCompass: false,
}));
const Map = ({ children }) => {
const containerEl = useRef(null);
const [mapReady, setMapReady] = useState(false);
const mapboxAccessToken = useAttributePreference('mapboxAccessToken');
useEffect(() => {
mapboxgl.accessToken = mapboxAccessToken;
}, [mapboxAccessToken]);
useEffect(() => {
const listener = ready => setMapReady(ready);
addReadyListener(listener);
return () => {
removeReadyListener(listener);
};
}, []);
useLayoutEffect(() => {
const currentEl = containerEl.current;
currentEl.appendChild(element);
if (map) {
map.resize();
}
return () => {
currentEl.removeChild(element);
};
}, [containerEl]);
return (
<div style={{ width: '100%', height: '100%' }} ref={containerEl}>
{mapReady && children}
</div>
);
};
export default Map;
I am fetching coordinates from api endpoint using socket controller, there is redux store that handle the changes in the data, however the position of the icons changes but its not smooth ,i have been trying to make it done since 5 days but i dont find any way how to do it, i am not finding mapbox documentation helpful
Below is the position map component , here the positions are being refreshed and updated to new cordinates, but i want to animate the changes on screen like movement of car on uber/ola app.
import React, { useCallback, useEffect } from 'react';
import ReactDOM from 'react-dom';
import mapboxgl from 'mapbox-gl';
import { Provider, useSelector } from 'react-redux';
import { map } from './Map';
import store from '../store';
import { useHistory } from 'react-router-dom';
import StatusView from './StatusView';
const PositionsMap = ({ positions }) => {
const id = 'positions';
const history = useHistory();
const devices = useSelector(state => state.devices.items);
const createFeature = (devices, position) => {
const device = devices[position.deviceId] || null;
return {
deviceId: position.deviceId,
name: device ? device.name : '',
category: device && (device.category || 'default'),
}
};
const onMouseEnter = () => map.getCanvas().style.cursor = 'pointer';
const onMouseLeave = () => map.getCanvas().style.cursor = '';
const onClickCallback = useCallback(event => {
const feature = event.features[0];
let coordinates = feature.geometry.coordinates.slice();
while (Math.abs(event.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += event.lngLat.lng > coordinates[0] ? 360 : -360;
}
const placeholder = document.createElement('div');
ReactDOM.render(
<Provider store={store}>
<StatusView deviceId={feature.properties.deviceId} onShowDetails={positionId => history.push(`/position/${positionId}`)} />
</Provider>,
placeholder
);
new mapboxgl.Popup({
offset: 20,
anchor: 'bottom-left',
closeButton: false,
className: 'popup'
})
.setDOMContent(placeholder)
.setLngLat(coordinates)
.addTo(map);
}, [history]);
useEffect(() => {
map.addSource(id, {
'type': 'geojson',
'data': {
type: 'FeatureCollection',
features: [],
}
});
map.addLayer({
'id': id,
'type': 'symbol',
'source': id,
'layout': {
'icon-image': '{category}',
'icon-allow-overlap': true,
'text-field': '{name}',
'text-allow-overlap': true,
'text-anchor': 'bottom',
'text-offset': [0, -2],
'text-font': ['Roboto Regular'],
'text-size': 12,
}
});
map.on('mouseenter', id, onMouseEnter);
map.on('mouseleave', id, onMouseLeave);
map.on('click', id, onClickCallback);
return () => {
Array.from(map.getContainer().getElementsByClassName('mapboxgl-popup')).forEach(el => el.remove());
map.off('mouseenter', id, onMouseEnter);
map.off('mouseleave', id, onMouseLeave);
map.off('click', id, onClickCallback);
map.removeLayer(id);
map.removeSource(id);
};
}, [onClickCallback]);
useEffect(() => {
map.getSource(id).setData({
type: 'FeatureCollection',
features: positions.map(position => ({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [position.longitude, position.latitude]
},
properties: createFeature(devices, position),
}))
});
}, [devices, positions]);
return null;
}
export default PositionsMap;
can any body help on thin to figure what i have been missing
I need to render a map using Mapbox only when data is ready.
I have the following code in my Vuex store:
/store/index.js
import Vue from "vue";
import Vuex from "vuex";
import _ from "lodash";
import { backendCaller } from "src/core/speakers/backend";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// Activity
activity: [],
geoIps: [],
},
mutations: {
// Activity
setActivity: (state, value) => {
state.activity = value;
},
setGeoIp: (state, value) => {
state.geoIps.push(value);
},
},
actions: {
// Activity
async FETCH_ACTIVITY({ commit, state }, force = false) {
if (!state.activity.length || force) {
await backendCaller.get("activity").then((response) => {
commit("setActivity", response.data.data);
});
}
},
async FETCH_GEO_IPS({ commit, getters }) {
const geoIpsPromises = getters.activityIps.map(async (activityIp) => {
return await Vue.prototype.$axios
.get(
`http://api.ipstack.com/${activityIp}?access_key=${process.env.IP_STACK_API_KEY}`
)
.then((response) => {
return response.data;
});
});
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
});
});
},
},
getters: {
activityIps: (state) => {
return _.uniq(state.activity.map((activityRow) => activityRow.ip));
},
},
strict: process.env.DEV,
});
In my App.vue I fetch all APIs requests using an async created method.
App.vue:
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: "App",
async created() {
await this.$store.dispatch("FETCH_ACTIVITY");
await this.$store.dispatch("FETCH_GEO_IPS");
},
};
</script>
In my Dashboard component I have a conditional rendering to draw the maps component only when geoIps.length > 0
Dashboard.vue:
<template>
<div v-if="geoIps.length > 0">
<maps-geo-ips-card />
</div>
</template>
<script>
import mapsGeoIpsCard from "components/cards/mapsGeoIpsCard";
export default {
name: "dashboard",
components: {
mapsGeoIpsCard,
},
computed: {
activity() {
return this.$store.state.activity;
},
activityIps() {
return this.$store.getters.activityIps;
},
geoIps() {
return this.$store.state.geoIps;
},
};
</script>
Then I load the Maps component.
<template>
<q-card class="bg-primary APP__card APP__card-highlight">
<q-card-section class="no-padding no-margin">
<div id="map"></div>
</q-card-section>
</q-card>
</template>
<script>
import "mapbox-gl/dist/mapbox-gl.css";
import mapboxgl from "mapbox-gl/dist/mapbox-gl";
export default {
name: "maps-geo-ips-card",
computed: {
geoIps() {
return this.$store.state.geoIps;
},
},
created() {
mapboxgl.accessToken = process.env.MAPBOX_API_KEY;
},
mounted() {
const mapbox = new mapboxgl.Map({
container: "map",
center: [0, 15],
zoom: 1,
});
this.geoIps.map((geoIp) =>
new mapboxgl.Marker()
.setLngLat([geoIp.longitude, geoIp.latitude])
.addTo(mapbox)
);
},
};
</script>
<style>
#map {
height: 500px;
width: 100%;
border-radius: 25px;
overflow: hidden;
}
</style>
The problem is that when the function resolves the first IP address, the map is drawn showing only one address and not all the others like this:
What is the best way to only draw the map when my FETCH_GEO_IPS function has finished?
Thanks in advance
I think the answer lies in this bit of code:
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
});
});
Your map function loops through every element of the array and commits each IP one by one. So when the first one is committed, your v-if="geoIps.length > 0" is true.
A workaround would be to set a flag only when the IPs are set.
This is a proposed solution:
import Vue from "vue";
import Vuex from "vuex";
import _ from "lodash";
import { backendCaller } from "src/core/speakers/backend";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// Activity
activity: [],
geoIps: [],
isReady: false
},
mutations: {
// Activity
setActivity: (state, value) => {
state.activity = value;
},
setGeoIp: (state, value) => {
state.geoIps.push(value);
},
setIsReady: (state, value) => {
state.isReady = value;
}
},
actions: {
// Activity
async FETCH_ACTIVITY({ commit, state }, force = false) {
if (!state.activity.length || force) {
await backendCaller.get("activity").then((response) => {
commit("setActivity", response.data.data);
});
}
},
async FETCH_GEO_IPS({ commit, getters }) {
let tofetch = getters.activityIps.length; // get the number of fetch to do
const geoIpsPromises = getters.activityIps.map(async (activityIp) => {
return await Vue.prototype.$axios
.get(
`http://api.ipstack.com/${activityIp}?access_key=${process.env.IP_STACK_API_KEY}`
)
.then((response) => {
return response.data;
});
});
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
toFetch -= 1; // decrement after each commit
if (toFetch === 0) {
commit("setIsReady", true); // all commits are done
}
});
});
},
},
getters: {
activityIps: (state) => {
return _.uniq(state.activity.map((activityRow) => activityRow.ip));
},
},
strict: process.env.DEV,
});
And in your view:
<template>
<div v-if="isReady">
<maps-geo-ips-card />
</div>
</template>
<script>
import mapsGeoIpsCard from "components/cards/mapsGeoIpsCard";
export default {
name: "dashboard",
components: {
mapsGeoIpsCard,
},
computed: {
activity() {
return this.$store.state.activity;
},
activityIps() {
return this.$store.getters.activityIps;
},
isReady() {
return this.$store.state.isReady;
},
};
</script>
I'm working on a simple implementation of a map using Google Maps API in a React project. I'm showing an info window to the user when they click a marker based on the state of the infoWindowStatus in the state object. If infoWindowStatus is true the info window displays.
If I mutate state directly and use a forceUpdate, the info window shows correctly. When I use setState, the info window does not show. (However, I can see that the infoWindowStatus DOES change from false to true when I console.log this.state.locations[myKey].infoWindowStatus.)
The problem I'm having is:
Using this.setState with update from immutability-helper is not re-rendering React Component.
import update from 'immutability-helper';
state = {
locations : [
{
position: {lat: 41.3029876, lng: -72.9191306},
title: "Pepe's Pizza",
mappy: '',
infoWindowStatus: false,
key : 0,
infoWindow :
{
content : "Loading...",
contentUrl : ""
}
}
]
}
// THIS DODGEY CODE WORKS
this.state.locations[myKey].infoWindowStatus = true;
this.forceUpdate()
//THIS CODE DOES NOT WORK
this.setState(
{ locations: update(this.state.locations, { [myKey] : {infoWindowStatus:
{$set:true}}})
}
);
The Entire Component as Requested is:
import React, { Component } from 'react';
import './css/App.css';
import './css/Custom.css';
import NeighborhoodMap from './neighborhoodMap';
import 'typeface-roboto';
import escapeRegEx from 'escape-string-regexp';
import update from 'immutability-helper';
class App extends Component {
state = {
locations : [
{
position: {lat: 41.3029876, lng: -72.9191306},
title: "Pepe's Pizza",
mappy: '',
infoWindowStatus: false,
key : 0,
infoWindow :
{
content : "Loading...",
contentUrl : ""
}
}
]
}
// TODO: separate data into external data file.
componentDidMount(){
this.setState({
filteredLocationsOnly : this.state.locations
})
}
// Launches Info Window on Google Map
showInfoWindowNow(locationSelected){
let myKey;
this.state.locations.filter( (location) =>{
if (locationSelected.name === location.title || locationSelected.title === location.title){
myKey = location.key;
return location
}
} );
this.updateInfoWindowContentAgain(myKey);
// // THIS CODE DOES NOT WORK AT ALL
// this.setState({ locations[myKey].infoWindowStatus : true })
// // THIS CODE DOESN'T SHOW THE INFO WINDOW
// console.log("Status: ", this.state.locations[myKey].infoWindowStatus);
// const tempLocations = [...this.state.locations];
// tempLocations[myKey] = { ...tempLocations[myKey], infoWindowStatus: true };
//
// this.setState(
// {
// locations: tempLocations
// }
// );
// console.log("Status Now: ", this.state.locations[myKey].infoWindowStatus);
// THIS DODGEY CODE WORKS
// https://stackoverflow.com/questions/51250518
this.state.locations[myKey].infoWindowStatus = true;
this.forceUpdate()
} //showInfoWindowNow
// Close Info Window on Google Map
closeInfoWindowNow(locationSelected){
this.forceUpdate()
}
// Update Content for Info Window
updateInfoWindowContentAgain(myKey){
return this.getInfoWindowContent(this.state.locations[myKey].title, myKey);
}
// Update Content for Info Window sub-function
getInfoWindowContent(searchTerm, myKey){
var nytAuthKey = "3d6801dab968446787ea71d5042ad8f7";
var myNewYorkTimesUrl = `https://api.nytimes.com/svc/search/v2/articlesearch.json?&api-key=${nytAuthKey}&q=${searchTerm}`
var contentForLocation;
var contentUrl;
let content = fetch(myNewYorkTimesUrl)
.then(response => response.json() )
.then(data => {
return addArticles(data);
}
)
.catch(error => requestError(error, 'articles'));
// add text from fetch request
function addArticles(data){
if (data.response && data.response.docs && data.response.docs.length > 1){
const articles = data.response.docs;
// var content, contentUrl;
let infoWindow = {};
articles.map(article => {
infoWindow.content = `${article.snippet}`;
infoWindow.contentUrl = `${article.web_url}`;
contentForLocation = `${article.snippet}`;
contentUrl = `${article.web_url}`;
return infoWindow;
});
}
} //addArticles
// Handle Errors
function requestError(error, part) {
console.log("Error: ", error);
}
content.then( content => {
this.state.locations[myKey].infoWindow.content = (contentForLocation);
this.state.locations[myKey].infoWindow.contentUrl = contentUrl;
this.forceUpdate()
}
)} // getInfoWindowContent
// end Nyt
filterLocations(query){
const match = new RegExp(escapeRegEx(query), 'i')
let showingLocations = this.state.locations.filter((location) => {
return match.test(location.title);
});
this.setState({
filteredLocationsOnly : showingLocations
})
} // end filterLocations
clearQuery = () => {
this.setState({query : ''});
}
updateQuery = (query) => {
this.setState({query : query.trim()})
this.filterLocations(query);
}
render() {
return (
<div className="App">
<NeighborhoodMap
menuOpen = {this.state.menuOpen}
locations = {this.state.locations}
filteredLocationsOnly = {this.state.filteredLocationsOnly}
query = {this.state.query}
updateQuery = { (query) => {
this.updateQuery(query)
}}
clearQuery = { () => {
this.clearQuery()
}}
filterLocations = { (query) => {
this.filterLocations(query)
}}
infoWindowStatus = {this.state.infoWindowStatus}
showInfoWindowNow = { (location) => {
this.showInfoWindowNow(location)
}}
closeInfoWindowNow = { (location) => {
this.closeInfoWindowNow(location)
}}
updateInfoWindowContentAgain = { (id) => {
this.updateInfoWindowContentAgain(id)
}}
infoWindow = {this.state.infoWindow}
/>
</div>
) // return
}// render
} // Component
export default App;
You could skip the immutability helper and instead copy your locations array, create a copy of locations[myKey], and overwrite infoWindowStatus:
const locations = [...this.state.locations];
locations[myKey] = { ...locations[myKey], infoWindowStatus: true };
this.setState({ locations });
Also, the infoWindowStatus you give to your NeighborhoodMap is from this.state.infoWindowStatus, but you store it in this.state.locations[0].infoWindowStatus.