I am currently trying to close my callout when I press the X button of a card on my app.
My code is this. ** function of closing card **
unsetCard = id => {
this.setState({
...this.state,
showCard: false,
});
this.markers.hideCallout();
if (this.state.keyboard) {
Keyboard.dismiss();
}
};
And this is my ** Map View code, I use RN Clustering **
<MapView
//
mapRef={ref => (this.myMapRef = ref)}
//
onPress={this.unsetCard}>
{this.props.data.map(marker => (
<Marker
key={marker.id}
ref={ref => (this.markers = ref)}
//
}>
<Callout
//
}}>
<CustomCallout title={marker.t} />
</Callout>
</Marker>
))}
</MapView>
Finally the function of unset card is called in this component in the same file:
<CustomCardWithImage
close={() => this.unsetCard(this.state.cardInfo.id)}
/>
I would appreciate if someone told me how to use the ref to the marker, because as much as I try it does not work.
Thanks in advance,
After seriously considering not continuing with this app, I took a break and solved the problem. Here is how you can use show or hide callout if you are interested:
Initialize markers
constructor(props) {
super();
this.markers = [];
}
Create markers refs
<Marker
key={marker.id}
ref={ref => {
this.markers[marker.id] = ref;
}}>
Call where needed
this.markers[id].hideCallout();
I hope someone finds it helpful someday
Related
I am trying to control the carousel in the child component from the parent component.
I have used forward ref on the child component but its not working. Where am I going wrong?
Parent:
const CoachingCarousel = ({}) => {
const carouselRef = useRef<Lottie>(null);
const renderItem = ({item}: any) => {
return (
<View style={styles.renderItemContainer}>
{item.icon}
<Text style={[styles.titletext, spacing.gbMt7]} variant="titleLarge">
{item.title}
</Text>
<Text style={[styles.subtitleText, spacing.gbMt4]} variant="bodyMedium">
{item.text}
</Text>
<Text
style={[styles.next]}
variant="bodyLarge"
onPress={() =>
carouselRef?.current?.goToSlide(
totalSlides !== item.key
? item.key
: () => {
setCoachingScreenCompleted('CoachingScreenCompleted', true),
console.log('Go to homepage');
},
)
}>
{totalSlides !== item.key ? 'Next tbc' : 'Done tbc'}
</Text>
</View>
);
};
return (
<AppCarousel slides={slides} renderItem={renderItem} ref={carouselRef} />
);
};
Child:
const AppCarousel = React.forwardRef(
({style, slides, renderItem}: props, ref) => {
return (
<View style={[styles.container, style]}>
<AppIntroSlider
ref={ref}
renderItem={renderItem}
data={slides}
/>
</View>
);
},
);
Here a is React rule,
1. Do not declare components within other components, this will lead to very weird behaviors in React.
2. Also you cannot share ref between two components at the same time whether they are parent/child or siblings.
Answer
Also from what I am seeing, you are using a ref to keep track of the current slide. ref does not rerender in React so you will not see your changes. Try using useState.
useRef is for non rerendering values or to keep track of DOM nodes which is not necessary in your case.
I found this tutorial to be quite well. One critic would be the over complicated use of React.children.map and React.children.clone although they have their use cases.
im using react-leaflet in Next js but when reloading page shows "window is not defined" even i am using dynamic import with ssr:false,
i saw this question made by others here and tried answers that they offered but didn't work, also tried to make the map mounted after component but again no result,
my code:
function ContactPage() {
const MapWithNoSSR = dynamic(() => import('../Map/Map'), {
ssr: false,
})
return (
<div className={styles.map}>
<MapWithNoSSR/>
</div>
)
}
function Map(){
const [markers, setMarkers]= useState([
{cord:[12.3774729,20.446257]},
{cord:[45.3774729,10.45224757]},
{cord:[40.3774729,15.4364757]},
])
<MapContainer center={[12.374729,22.4464757]} zoom={13} scrollWheelZoom={true} style={{height: "100%", width: "100%",zIndex:'1'}}>
<TileLayer
url={`example.com`}
attribution='Map data © <a>Mapbox</a>'
/>
{markers?.map((element,idx)=>{
return <Marker
position={element?.cord}
draggable={true}
animate={true}
key={idx}
>
<Popup>
Test PopUP
</Popup>
</Marker>
})}
</MapContainer>
}}
}
as you were told in the comment, dynamic () has to go outside of the component or screen you are going to return, e. g.
import dynamic from "next/dynamic"
const MyAwesomeMap = dynamic(() => import("../components/Map/index"), { ssr:false })
export default function inicio() {
return(
<>
<p>Example Map</p>
<MyAwesomeMap />
</>
)
}
Your Map component doesn't return anything
I have custom markers being rendered to my map and do so exactly as I want. I'm using Custom Marker which is a View, Image, and then text specific to an event over the image.
For some reason using a custom marker view is blocking the onPress on the Marker component.
I've tested the onPress event as similiar to below for testing:
<Marker onPress={() => {console.log('Pressed')}}> ... but only logs the press message when I remove my custom marker view inside the Marker component.
Is there a reason why the custom marker view would be stopping the onPress? How do I get around this and fire an onPress event for a custom marker?
Added full code below for better look. Right now it is working but only because I am using the onSelect event for iOS. The onPress works the way it is for Android but not iOS.
<MapView
style={styles.map}
ref={ map => {this.map = map }}
region={this.state.region}
onRegionChangeComplete={this.onRegionChange}
rotateEnabled={false}
loadingEnabled={true}
>
{matches.map((marker, index) => {
return (
<MapMarker
key={index}
mapMarker={marker}
handlePress={() => {
this.map.animateToRegion({
latitude: marker.location.latlng.latitude,
longitude: marker.location.latlng.longitude
}, 100)
}}
/>
)
})}
</MapView>
MapMarker.js
render() {
const { mapMarker } = this.props;
return (
<Marker
ref={(ref) => {this.markerRef = ref; }}
coordinate={mapMarker.location.latlng}
title={mapMarker.location.streetName}
stopPropagation={true}
pointerEvents='auto'
onPress={() => console.log('pressed')}
onSelect={() => {
this.props.handlePress();
}}
>
<CustomMapMarker
gameType={mapMarker.matchType}
isSanctioned={mapMarker.isSanctioned}
startDate={mapMarker.startAt}
/>
<Callout style={styles.customCallout}>
<CustomMarkerCallout markerInfo={mapMarker} />
</Callout>
</Marker>
);
}
Fixed the issue by adding onSelect which is iOS specific. The onPress was only working for Android so don't know if this is the right answer but is working for me as of now.
I have the map working when it loads, but whenever I pass props the map itself never changes.
The marker changes, but is always 1 state behind what it should be, obviously a sync issue but I'm not sure how to fix this in my component.
class Map extends React.Component {
state = {
center: { lat: 50.937531, lng: 6.960278600000038 }
}
componentWillReceiveProps = () => {
this.setState({
center: {
lat: Number(parseFloat(this.props.city.lat)),
lng: Number(parseFloat(this.props.city.lng))
}
});
}
render() {
console.log(this.state.center)
return (
<div>
<MapDetails
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyB-L-IikkM6BZ_3Z2QIzbkx4pdAkP76tok&v=3.exp&libraries=geometry,drawing,places"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100vh`}} />}
mapElement={<div style={{ height: `100%` }} />}
mapCenter={this.state.center}
/>
</div>
)
}
}
const mapStateToProps = (state) => {
return { city: state.CityNameReducer }
}
export default connect(mapStateToProps)(Map);
const MapDetails = withScriptjs(withGoogleMap(props =>
<GoogleMap
defaultZoom={12}
defaultCenter={props.mapCenter}
>
<Marker
position={props.mapCenter}
/>
</GoogleMap>
));
The biggest question I have is why the map itself is not updating?
Add a key to GoogleMap component. a Key should be unique each time provided. for your testing use new Date().getTime() function.
<GoogleMap key={new Date().getTime()}/>
as I mentioned its for only testing so make sure to provide this key in better way
I may recommend you to use shouldComponentUpdate(nextProps, nextState) {} instead of componentWillReceiveProps().
I recommend you to read the following page, to understand how it works.
Updating and componentWillReceiveProps
I don't have more information about what is happening in the parent of your component, so I cannot go deeper to the problem. But I think you can get the solution by changing that.
Im using "google-maps-react" and trying to add new markers to my map with clicks.
I currently manage to console log the specific latLng, but cant seem to make a new one. I'm pretty new to React.
My onMapClick works with finding the latitude and longitude. But I think I need to add the position to an array and then use that one to update the map. Might be wrong
onMapClick = (map,maps,e) => {
const { latLng } = e;
const latitude = e.latLng.lat();
const longitude = e.latLng.lng();
console.log(latitude + ", " + longitude);
var marker = new window.google.maps.Marker({
position: e.latLng,
setMap: map,
});
}
The solution Im currently on is that I just hardcoded some Markers in my render() with the location of array in Marker
<Marker
onClick={this.onMarkerClick}
name={storedLocations[0]}
position={{lat:listLatitude[0], lan:listLongitude[0]}}
/>
My InfoWindow is:
<InfoWindow
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
onClose={this.onClose}
>
<div>
<h4>{this.state.selectedPlace.name}</h4>
</div>
</InfoWindow>
</Map>
My onMapClick works with finding the latitude and longitude. But I
think I need to add the position to an array and then use that one to
update the map.
That's indeed would the React way but instead of instantiating markers via Google Maps API, consider to keep the data (marker locations) via state and React will do the rest like this:
class MapContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
locations: []
};
this.handleMapClick = this.handleMapClick.bind(this);
}
handleMapClick = (ref, map, ev) => {
const location = ev.latLng;
this.setState(prevState => ({
locations: [...prevState.locations, location]
}));
map.panTo(location);
};
render() {
return (
<div className="map-container">
<Map
google={this.props.google}
className={"map"}
zoom={this.props.zoom}
initialCenter={this.props.center}
onClick={this.handleMapClick}
>
{this.state.locations.map((location, i) => {
return (
<Marker
key={i}
position={{ lat: location.lat(), lng: location.lng() }}
/>
);
})}
</Map>
</div>
);
}
}
Explanation:
locations state is used to track all the locations once the map is clicked
locations state is passed to Marker component to render markers
The next step could be to introduce a separate component for markers list, Thinking in React tells about components the follows:
One such technique is the single responsibility principle, that is, a
component should ideally only do one thing. If it ends up growing, it
should be decomposed into smaller subcomponents.
const MarkersList = props => {
const { locations, ...markerProps } = props;
return (
<span>
{locations.map((location, i) => {
return (
<Marker
key={i}
{...markerProps}
position={{ lat: location.lat(), lng: location.lng() }}
/>
);
})}
</span>
);
};
Here is a demo which demonstrates how to add a makers on map click via google-maps-react library