I've been baffled by this recently, and I'm looking to dynamically update map marker images based on whether a map marker is selected/active or not (Each MapMarker has a category of which references either the active image or the default image from two collections of images; interestIcons and interestIconsSelected. Essentially I have a collection of MapMarkers that are rendered out with mapping through the map markers collection. Each MapMarker is a child component of MapView.Marker.
I would like to render out all MapMarkers in a default non-selected / non-active state having the default image from interestIcons, when selected, that MapMarker image should change to the active image from interestIconsSelected, when another MapMarker is selected the previous selected should revert back to the default image, and the new should change to the selected image.
Currently, I'm able to render out the map markers with the default image but the when selecting the mapmarker the image does not seem to change immediately unless you were to zoom out / zoom in again i.e. to cause some kind of re-render, I would like it so clicking on a MapMarker would immediately cause the image to update please.
MapScreen.js: Renders out MapView for all of the MapView.Marker MapMarkers through a map.
<MapView
ref={map => { this._map = map }}
style={Styles.Map.map}
initialRegion={this.props.region}
onRegionChange={this.handleRegionChange}
>
{
this.props.events
.filter(this.eventFilterTimeNearFuture)
.filter(this.eventFilterTimeNotPast)
.filter(this.eventFilterDistance)
.filter(this.eventFilterInterest)
.map(e =>
<MapView.Marker
key={e.id}
onPress={() => this.handleLocationPush(e)} // Set selected event state
coordinate={e.location}
>
<MapMarker
event={e}
size={this.state.markerSize}
selected={this.state.selectedEvent} // The selected event set by state call.
/>
</MapView.Marker>
)
}
{ !this.props.regionMock &&
<MapView.Marker
key={'userLocation'}
coordinate={this.props.region}
>
<MapMarker size={'user'} />
</MapView.Marker>
}
</MapView>
MapMarker.js
import {interestIcons, interestColors, interestIconsSelected} from "../../utils/Icons";
import {Styles} from '../../StyleProvider';
class MapMarker extends React.Component {
constructor() {
super();
this.state = {
initialized: false,
active: false,
};
};
componentWillReceiveProps(nextProps) {
if (!this.state.initialized) {
console.log('initialization');
this.setState({initialized: true});
}
else {
// If the nextProps.selected prop exists which it will
if (nextProps.selected) {
// If the nextProps.selected props id equals the this event id then selected else non-selected.
if (nextProps.selected.id === nextProps.event.id) {
console.log('SELECTED: ' + JSON.stringify(nextProps.selected));
// set staae to active
this.setState({
active: true
});
console.log(interestIconsSelected[nextProps.event.interest[0]]);
} else {
// set state to not active
// console.log('NON-SELECTED: ' + JSON.stringify(nextProps.event));
this.setState({
active: false
});
}
this.forceUpdate();
}
}
}
markerIcon(interest) {
return this.state.active ? interestIconsSelected[interest] : interestIcons[interest];
}
renderIcon() {
if (this.props.event.type === 'Event') {
return (
<Image
source={this.markerIcon(this.props.event.interest[0])}
style={Styles.MapMarker.eventImage}
/>
)
}
}
The componentWillReceiveProps(nextProps) is still a work in progress and that indicates 'well enough' the current selected event and al the non-selected events.
I've attempted to set image source to use say this.state.image
and then setting the image state in componentWillReceiveProps respectively i.e.
if (nextProps.selected) {
// If the nextProps.selected props id equals the this event id then selected else non-selected.
if (nextProps.selected.id === nextProps.event.id) {
console.log('SELECTED: ' + JSON.stringify(nextProps.selected));
// set staae to active
this.setState({
active: true,
image: this.markerIcon(nextProps.event.interest[0], true)
});
console.log(interestIconsSelected[nextProps.event.interest[0]]);
} else {
// set state to not active
console.log('NON-SELECTED: ' + JSON.stringify(nextProps.event));
this.setState({
active: false,
image: this.markerIcon(nextProps.event.interest[0], false)
});
}
}
renderIcon() {
if (this.props.event.type === 'Event') {
return (
<Image
source={this.state.image}
style={Styles.MapMarker.eventImage}
/>
)
}
}
Change image state does seem to work more effectively in that the image would change immediately, but it then seems that the image on initial render wouldn't be set at all, so it would be just an empty icon until selected.
Thanks very much, appreciate any help at all.
Update: Attempted defining Image component under MapView.Marker and this does not work.
this.state.markers
.map(e =>
<MapView.Marker
key={e.id}
onPress={() => this.handleLocationPush(e)}
coordinate={e.location}
>
{/* <MapMarker
event={e}
size={this.state.markerSize}
/> */}
<Image
source={this.state.selectedEvent === e ? interestIconsSelected[e.interest[0]] : interestIcons[e.interest[0]]}
style={Styles.MapMarker.eventImage}
/>
</MapView.Marker>
)
BUT this works although you don't appear to be able to apply styling to MapView.Marker but this isn't an implementation I would like as I would like to keep the custom MapMarker component
this.state.markers
.map(e =>
<MapView.Marker
key={e.id}
onPress={() => this.handleLocationPush(e)}
coordinate={e.location}
image={this.state.selectedEvent === e ? interestIconsSelected[e.interest[0]] : interestIcons[e.interest[0]]}
/>
)
The above two snipets of code with either using the image prop directly on MapView.Marker or having an Image component directly under MapView.Marker are no good as using a MapMaper Child Component.
you use componentWillReceiveProps
life cycle method it did not run at first render also you use this.state.initialized
with is false in constructor
state so this will make you need twice clicks to make it active
componentWillReceiveProps(nextProps) { // it did not run at first render
if (!this.state.initialized) { // this.state.initialized with is false in constructor
console.log('initialization');
this.setState({initialized: true});
}
.......
}
you can remove completely your componentWillReceiveProps
if you do something like this
markerIcon() { //did not get interest directly instead access it from props
return this.props.selected.id === this.props.event.id ?
interestIconsSelected[this.props.event.interest[0]]
:
interestIcons[this.props.event.interest[0]];
}
here you compare two objects with identical comparison as the are deep you can use some thing like this instead to not get babel confused see more
<Image
// use this.state.selectedEvent.id === e.id instead of this.state.selectedEvent === e
source={this.state.selectedEvent.id === e.id ? interestIconsSelected[e.interest[0]] : interestIcons[e.interest[0]]}
style={Styles.MapMarker.eventImage}
/>
I hope that helps Thanks
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With