簡體   English   中英

子組件不會隨着更改而重新呈現

[英]Child component not re-rendering with changes

我有兩個組件,它們都是同一個父組件的子組件,它們都呈現一個地點列表-一個將地點作為標記加載到地圖上的地圖,然后是帶有過濾菜單的地點列表網格。 我想要做的是將地點列表組件中的過濾器單擊傳達給地圖組件以過濾標記。 為此,我在父組件中有一個名為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> ) } } } 

這是Map的子組件:

 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); 

在子組件中,您正在構造器中檢索/設置位值。 之后,除非添加getDerivedStateFromProps(props,state) ,否則Parent組件的prop的任何更改都不會通知Child組件狀態值已更新。

通過這種方法,您將收到新的道具並從新接收的道具更新狀態。

在這里更新狀態后(使用setState,將執行子組件的render方法)

為了重新渲染組件並顯示更改,狀態需要更新。 現在,您用初始道具更新狀態。 當道具更改時,子組件的狀態不會改變,因為您僅使用初始道具。 因此,您可以做的是使用生命周期掛鈎componentWillReceiveProps並在其中使用新的道具更新狀態。 代碼可以是這樣的:

componentWillReceiveProps(nextProps){
  if(this.state.lat !== nextProps.center.latitude){
    this.setState({ lat: nexrProps.center.latitude});
  }
}

您也可以對其余變量進行同樣的處理。 這樣,無論何時道具更改,您的狀態也會改變,從而迫使組件重新渲染並反映更改。

希望能幫助到你!!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM