我有两个组件,它们都是同一个父组件的子组件,它们都呈现一个位置列表-一个将位置作为标记加载到地图上的地图,然后是带有过滤菜单的位置列表网格。我想要做的是将地点列表组件中的过滤器单击传达给地图组件以过滤标记。为此,我在父组件中有一个名为handlePlaceFilter()的函数,我将该函数作为道具传递到地方列表子组件中。
在过滤器单击子级后,我能够从父级组件触发控制台日志语句,并且可以将经过过滤的位置列表传递给父级-但是我无法通过以下方式重新渲染任何一个组件:过滤后的地点清单。
这是既包含子项又包含handlePlaceFilter()函数的父组件:
import React from 'react';
import Header from './Header';
import MapContainer from './MapContainer';
import _ from 'lodash';
import Places from './Places';
const Cosmic = require('cosmicjs')();
export default class PlaceIndex extends React.Component {
constructor (props) {
super(props);
this.handlePlaceFilter = this.handlePlaceFilter.bind(this);
this.state = {
destination: '',
destinations: '',
places: '',
globals: '',
}
}
async componentDidMount() {
const bucket = Cosmic.bucket({
slug: 'where-she-goes',
read_key: '',
write_key: ''
});
try {
let result = await bucket.getBucket()
this.setState (() => {
return {
destination: _.find(result.bucket.objects, { slug: this.props.match.params.slug }),
destinations: _.filter(result.bucket.objects, {type_slug: 'destinations'}),
places: _.filter(result.bucket.objects, {type_slug: 'places'}),
globals: _.filter(result.bucket.objects, {type_slug: 'globals'})
}
});
} catch (err) {
console.log(err)
}
}
handlePlaceFilter (places) {
console.log("Place filter clicked!")
console.log(places)
this.setState (() => {
return {
places: places
}
});
}
render() {
if (!this.state.places || !this.state.destination)
return <p>Loading...</p>
// compile list of destination plus children
let placeDestinations = new Array();
placeDestinations.push(this.state.destination.slug);
this.state.destination.metadata.child_destinations &&
this.state.destination.metadata.child_destinations.map(destination => {
placeDestinations.push(destination.slug)
destination.metadata.child_destinations &&
destination.metadata.child_destinations.map(child_destination => {
placeDestinations.push(child_destination.slug)
})
})
console.log("Destination List")
console.log(placeDestinations)
// filter places by destination list
let places = this.state.places.filter(function(place) {
return placeDestinations.includes(place.metadata.destination.slug);
})
console.log("Places")
console.log(places)
let destinationCenter = {
latitude: this.state.destination.metadata.latitude,
longitude: this.state.destination.metadata.longitude
}
return (
<div>
<Header
destinations={this.state.destinations}
globals={this.state.globals}
/>
<div className="places-title text-center">
<h2>All Places in {this.state.destination.title}</h2>
</div>
<MapContainer
places={places}
center={destinationCenter}
/>
<Places
places={places}
handlePlaceFilter={this.handlePlaceFilter}
/>
</div>
);
}
}
这是地方列表的子组件:
import React from 'react'
import _ from 'lodash'
export default class Places extends React.Component {
constructor (props) {
super(props);
this.showHotels = this.showHotels.bind(this);
this.showAll = this.showAll.bind(this);
this.showRestaurants = this.showRestaurants.bind(this);
let places = _.flatMap(this.props.places, this.props.places.metadata);
var allplaces = new Array();
var hotels = new Array();
var restaurants = new Array();
var sights = new Array();
places &&
places.map(place => {
allplaces.push(place)
if (place.metadata.place_type == 'Hotel') {
hotels.push(place)
}
if (place.metadata.place_type == 'Restaurant') {
restaurants.push(place)
}
if (place.metadata.place_type == 'Sight') {
sights.push(place)
}
})
// Limit # of places in each array to customize for page contect
if (this.props.limit) {
(allplaces.length > 0) ? (allplaces.length = this.props.limit) : allplaces;
(hotels.length > 0) ? (hotels.length = this.props.limit) : hotels;
(restaurants.length > 0) ? (restaurants.length = this.props.limit) : restaurants;
(sights.length > 0) ? (sights.length = this.props.limit) : sights;
}
this.state = {
current: "All",
places: allplaces,
hotels: hotels,
restaurants: restaurants,
sights: sights,
allplaces: allplaces
}
}
showAll (e) {
e.preventDefault();
this.props.handlePlaceFilter(this.state.allplaces);
this.setState (() => {
return {
current: "All",
places: this.state.allplaces
}
});
}
showHotels (e) {
e.preventDefault();
this.props.handlePlaceFilter(this.state.hotels);
this.setState (() => {
return {
current: "Hotels",
places: this.state.hotels
}
});
}
showRestaurants (e) {
e.preventDefault();
this.props.handlePlaceFilter(this.state.restaurants);
this.setState (() => {
return {
current: "Restaurants",
places: this.state.restaurants
}
});
}
showSights (e) {
e.preventDefault();
this.props.handlePlaceFilter(this.state.sights);
this.setState (() => {
return {
current: "Sights",
places: this.state.sights
}
});
}
render () {
if (this.state.allplaces.length > 0) {
return (
<div className="container">
<div className="row">
<div className="col-md-12">
<div className="blogFilter text-center text-uppercase">
<ul className="list-inline">
<li>{(this.state.current == "All") ? <a href="#" onClick={this.showAll} className="current">All</a> : <a href="#" onClick={this.showAll}>All</a>}</li>
<li>{(this.state.hotels.length > 0) ? ((this.state.current == "Hotels") ? <a href="#" className="current" onClick={this.showHotels}>Hotels</a> : <a href="#" onClick={this.showHotels}>Hotels</a>) : <span></span>}</li>
<li>{(this.state.restaurants.length > 0) ? ((this.state.current == "Restaurants") ? <a href="#" className="current" onClick={this.showRestaurants}>Restaurants</a> : <a href="#" onClick={this.showRestaurants}>Restaurants</a>) : <span></span>}</li>
<li>{(this.state.sights.length > 0) ? ((this.state.current == "Sights") ? <a href="#" className="current" onClick={this.showSights}>Sights</a> : <a href="#" onClick={this.showSights}>Sights</a>) : <span></span>}</li>
</ul>
</div>
<div className="row">
<div className="blogContainer">
{
this.state.places &&
this.state.places.map(place => {
console.log("Places")
console.log(place)
return (
<div className="col-sm-3 design">
<article className="portfolio portfolio-2 post-grid">
<div className="post-thumb">
<a href={`/place/${place.slug}`}><img src={place.metadata.hero.imgix_url} alt="" /></a>
<div className="post-thumb-overlay text-center">
<div className="text-uppercase text-center">
<a href="single-portfolio.html"><i className="fa fa-link"></i></a>
<a href={place.metadata.hero.imgix_url} ><i className="fa fa-search"></i></a>
</div>
</div>
</div>
<div className="post-content">
<header className="entry-header text-center text-uppercase">
<h6><a href={`/place/${place.slug}`}>{place.metadata.place_type}</a></h6>
<h2 className="entry-title"><a href=" ">{place.title}</a></h2>
</header>
</div>
</article>
</div>
)
})
}
</div>
</div>
</div>
</div>
</div>
)
} else {
return (
<div></div>
)
}
}
}
这是地图的子组件:
import React, { Component } from 'react';
import {Map, InfoWindow, Marker, GoogleApiWrapper} from 'google-maps-react';
const mapStyles = {
width: '100%',
height: '300px'
};
let geocoder;
export class MapContainer extends Component {
constructor (props) {
super(props);
this.onMarkerClick = this.onMarkerClick.bind(this);
this.displayMarkers = this.displayMarkers.bind(this);
let addresses = new Array();
this.props.places &&
this.props.places.map(place => {
addresses.push(place.metadata.address)
})
this.state = {
lat: this.props.center.latitude,
lng: this.props.center.longitude,
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
places: [],
addresses: addresses
}
}
componentDidMount () {
this.plotPoints()
}
plotPoints () {
let locations = this.getPoints(geocoder)
let places = new Array()
Promise.all(locations)
.then((returnVals) => {
returnVals.forEach((latLng) => {
let place = {
latitude: latLng[0],
longitude: latLng[1]
}
places.push(place)
})
console.log("Places to Plot:")
console.log(places[0].latitude)
// places now populated
this.setState(() => {
return {
lat: places[0].latitude,
lng: places[0].longitude,
places: places
}
});
console.log("Center Lat")
console.log(this.state.lat)
console.log(this.state.lng)
});
}
getPoints(geocoder) {
let locationData = [];
for (let i = 0; i < this.state.addresses.length; i++) {
locationData.push(this.findLatLang(this.state.addresses[i], geocoder))
}
return locationData // array of promises
}
findLatLang(address, geocoder) {
return new Promise(function(resolve, reject) {
geocoder.geocode({
'address': address
}, function(results, status) {
if (status === 'OK') {
console.log(results);
resolve([results[0].geometry.location.lat(), results[0].geometry.location.lng()]);
} else {
reject(new Error('Couldnt\'t find the location ' + address));
}
})
})
}
displayMarkers (stores) {
return stores.map((place, index) => {
return <Marker key={index} id={index} position={{
lat: place.latitude,
lng: place.longitude
}}
onClick={() => console.log("You clicked me!")} />
})
}
onMarkerClick (props, marker, e) {
this.setState({
selectedPlace: props,
activeMarker: marker,
showingInfoWindow: true
});
};
render() {
geocoder = new this.props.google.maps.Geocoder();
console.log("Place Array")
console.log(this.state.places)
return (
<div className="container place-map">
<div className="row">
<div className="col-md-12">
<Map
google={this.props.google}
zoom={8}
style={mapStyles}
initialCenter={{
lat: this.state.lat,
lng: this.state.lng
}}
>
{this.displayMarkers(this.state.places)}
<InfoWindow
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
>
<div>Your Location Here!</div>
</InfoWindow>
</Map>
</div>
</div>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: 'AIzaSyCOJDrZ_DXmHzbzSXv74mULU3aMu3rNrQc'
})(MapContainer);
答案 0 :(得分:0)
在子组件中,您正在构造器中检索/设置位值。之后,除非您添加getDerivedStateFromProps(props, state),否则Parent
组件的prop的任何更改都不会通知Child
组件状态值已更新。
在这种方法中,您将收到新的道具并从新接收的道具更新状态。
在此处更新状态后(使用setState,将执行子组件的render方法)
答案 1 :(得分:0)
要重新渲染组件并显示状态需要更新的更改,请执行以下操作。现在,您用初始道具更新状态。当道具更改时,子组件的状态不会改变,因为您仅使用初始道具。因此,您可以做的是使用生命周期挂钩componentWillReceiveProps
,并在其中使用新的道具更新状态。
代码可以是这样的:
componentWillReceiveProps(nextProps){
if(this.state.lat !== nextProps.center.latitude){
this.setState({ lat: nexrProps.center.latitude});
}
}
您也可以对其余变量进行同样的处理。 这样,只要您的道具发生变化,状态也会发生变化,从而迫使组件重新渲染并反映这些变化。
希望有帮助!