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.