问题
I'm very new to React and web development, so hopefully this makes sense. So I'm building an App that uses Google Maps api and React to show markers of places that I fetch from Foursquare.
I generate an array of places from Foursquare (Limited to 5 places that I want to keep static) and placed it in the state of my app so I can pass the data to both a List sidebar and my Map to generate markers. I have also created a Filter search bar that I want to use to filter the List AND the Map Markers so that ONLY the places in the list and the map markers match the search input of the users.
I managed to filter the List alright, but I'm struggling to find a way to filter the Map markers.
Now I'm thinking I need to move my places array out of the state and use the search input to filter and create a new array that I keep in the state, but I'm unsure how to do this... This is what I've done so far:
Filter.js
import React, { Component } from 'react';
class Filter extends Component {
filterUpdate() {
const val = this.myValue.value
this.props.filterUpdate(val)
}
render() {
return(
<header>
<form>
<input
type='text'
ref={ (value) => { this.myValue = value } }
placeholder='Type to filter...'
onChange={this.filterUpdate.bind(this)}
/>
</form>
</header>
)
}
}
export default Filter;
List.js
import React, { Component } from 'react';
class List extends Component {
render() {
const { places, filterText } = this.props;
const placeList = places
/*.filter(place => {
// remove places that do not match current filter text
return place.venue.name.toLowerCase().indexOf(filterText.toLowerCase()) >= 0
})
*/
.map(place => {
return (
<li key={place.venue.id}>
<p>{place.venue.name}</p>
<p>{place.venue.location.address}</p>
</li>
)
})
return(
<div className="list">
<ul>
{placeList}
</ul>
</div>
)
}
}
export default List;
Map.js
import React, { Component } from 'react'
class Map extends Component {
componentDidMount() {
this.loadMap();
}
loadMap = () => {
loadScript("https://maps.googleapis.com/maps/api/js?key=<INSERT API HERE>=initMap");
window.initMap = this.initMap;
// Shows alert when problem with auth, from: https://developers.google.com/maps/documentation/javascript/events#auth-errors
window.gm_authFailure = function() {
alert('Cannot load Google Maps! Please ensure that you have a valid Google Maps API key! Please go to https://developers.google.com/maps/documentation/javascript/get-api-key')
}
}
initMap = () => {
let map = new window.google.maps.Map(document.getElementById('map'), {
center: {lat: 52.637106, lng: -1.139771},
zoom: 15
});
this.props.places.map(place => {
const marker = new window.google.maps.Marker({
position: {lat: place.venue.location.lat, lng: place.venue.location.lng},
map: map,
title: place.venue.name,
animation: window.google.maps.Animation.DROP,
id: place.venue.id
});
});
}
render() {
return(
<div id="map"></div>
)
}
}
export default Map;
function loadScript(url) {
// https://stackoverflow.com/questions/34779489/rendering-a-google-map-without-react-google-map
let ref = window.document.getElementsByTagName('script')[0];
let script = window.document.createElement('script');
script.src = url;
script.async = true;
script.defer = true;
ref.parentNode.insertBefore(script, ref);
script.onerror = function () {
document.write('Load error: Google Maps')
};
}
App.js
import React, { Component } from 'react'
import Filter from './components/Filter'
import List from './components/List'
import Map from './components/Map'
import axios from 'axios'
class App extends Component {
constructor(props) {
super(props)
this.state = {
filterText: '',
places: []
}
}
filterUpdate(value) {
this.setState({
filterText: value
})
}
componentDidMount() {
this.loadPlaces()
}
loadPlaces = () => {
const endPoint = "https://api.foursquare.com/v2/venues/explore?";
const parameters = {
client_id: "<INSERT API HERE>",
client_secret: "5A3ANB4RL11MWKHBA2W0KYGKDIRHERLL1XRZE3JEH1BUS4V5",
v: "20180323",
query: "coffee",
near: "Leicester, UK",
limit: 5
}
// Get data for coffee places from Foursquare
axios.get(endPoint + new URLSearchParams(parameters))
.then(response => {
this.setState({
places: response.data.response.groups[0].items
})
})
.catch(error => {
alert("An error occurred fetching data from Foursquare: " + error);
})
}
render() {
const { places, filterText } = this.state;
const filterPlaces = places
.filter(place => {
return place.venue.name.toLowerCase().indexOf(filterText.toLowerCase()) >= 0
})
return (
<div className="App">
<Filter
filterText={this.state.filterText}
filterUpdate={this.filterUpdate.bind(this)}
/>
<main>
<List
places={filterPlaces}
filterText={this.state.filterText}
/>
<Map
places={filterPlaces}
/>
</main>
</div>
);
}
}
export default App;
index.css
body {
height: 100%;
margin: 0;
padding: 0;
font-family: sans-serif;
}
#map {
height: 100vh;
}
.list {
background-color: #fff;
opacity: 0.9;
z-index: 1;
text-align: left;
margin: 5px;
width: 25%;
position: absolute;
padding: 5px;
min-width: 120px;
height: 400px;
overflow-y: scroll;
top: 200px;
}
/* ##### Search ##### */
header {
background: #3498db;
}
input[type='text'] {
margin: 1rem;
background: #fff;
font-size: 1.3rem;
border: none;
box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.3);
border-radius: 3px;
padding: 0 2rem;
width: calc(100% - 3rem);
height: 4rem;
}
I don't think I can update the state from the render method... Can I even filter an array in the state to change which markers are showing? Help!
回答1:
SOME IDEAS
Save reference to map
(initMap) in component (this.map=map
) instance.
shouldComponentUpdate
will be called on filter changes (with new props).
Using saved map reference should be possible to remove all existing markers. From new props you can create new markers. It could be possible to optimize this proces, f.e. only hide filtered out markers.
Of course (as usual in sCU) compare this.props
with nextProps
to avoid unnecessary operations on map.
At the end of sCU return false to block rerendering - w/o that div with map will be cleared by render.
来源:https://stackoverflow.com/questions/52065982/how-to-filter-an-array-in-my-state-so-it-only-shows-map-markers-based-on-my-user