Actually, I am using react-leaflet with custom popup, when i tried to use react-router-dom Link component in it than i got error
"leaflet": "^1.4.0",
"leaflet.markercluster": "^1.3.1",
"react": "^16.5.2",
"react-dom": "^16.8.4",
"react-router-dom": "4.3.1",
"react-router-redux": "5.0.0-alpha.9",
React Router Link doesn't work with LeafletJS
I tried above mentioned link but still got error.
*
* CustomPopup
*
*/
import React from 'react';
import Moment from 'moment';
import {extendMoment} from "moment-range";
import {Link, withRouter} from "react-router-dom";
import currencyFormatter from "../../utils/currencyFormatter";
const moment = extendMoment(Moment);
const CustomPopup = (props) => {
const { item } = props;
return (
<div className='map-properties'>
<div className='map-img'>
<img
style={{width: "301px", height: "203px"}}
src={item.image ? `${item.image}`: "http://placehold.it/301x203"}
className="custom-img"
onError={(e)=>{e.target.onerror = null; e.target.src="http://placehold.it/301x203"}}
/>
</div>
<div className='map-content'>
<h4><a href='properties-details.html'> {item.title} </a></h4>
{item.address && <p className='address'> <i className='fa fa-map-marker'></i> {item.address} </p>}
<p className='description'> description </p>
<div className='map-properties-fetures'>
<span><i className='flaticon-square-layouting-with-black-square-in-east-area'></i>{item.area} sqft<sup>2</sup></span>
<span className="right"><i className='flaticon-bed'></i>{item.bedrooms}</span>
<span><i className='flaticon-holidays'></i>{item.bathrooms}</span>
<span className="right">
<i className="fa fa-calendar"></i> {moment.range(moment(item.created_at), moment()).diff('days')} Days ago
</span>
</div>
<div className='map-properties-btns'>
<a className='border-button-sm border-button-theme' style={{marginRight: "5px"}}>
{currencyFormatter(item.price)}
</a>
##below the React Router Dom component <Link/>##
<Link href='properties-details' className='button-sm button- theme'>Details</Link>
</div>
</div>
</div>
);
};
export default CustomPopup;
/**
*
*End of CustomPopup
*
*/
/**
*
* LeafLet Map Container where i use above custom popup
*
*/
import "./index.css";
import React, {Fragment, PropTypes } from 'react';
import CustomPopup from "../../components/CustomPopup";
import {createClusterCustomIcon, ThemePointer} from "../../components/CustomMarker";
import 'react-leaflet-markercluster/dist/styles.min.css';
import MarkerClusterGroup from 'react-leaflet-markercluster';
import {TileLayer, Marker, Popup, Map, LayersControl,} from 'react-leaflet';
import {withRouter} from "react-router-dom";
import ContextProvider from "../../components/ContextProvider";
export const Context = React.createContext({});
const MyPopupMarker = (props) => {
const { item } = props;
let lat = item.latitude ? item.latitude : 0.0000;
let lng = item.longitude ? item.longitude : 0.0000;
return (
<Marker position={[lat, lng]} icon={ThemePointer}>
<Popup>
<CustomPopup key={item.id} item={item} />
</Popup>
</Marker>
);
};
const MyMarkersList = (props) => {
const { markers } = props;
const items = markers && markers.length > 0 ? markers.map((item, index) => (
<MyPopupMarker key={item.id} item={item} context={props.context}/>
)): "";
return <Fragment>{items ? items : ""}</Fragment>;
};
const { BaseLayer,} = LayersControl;
class LeafLetMap extends React.PureComponent{
updateDimensions() {
const height = window.innerWidth >= 992 ? window.innerHeight : 400
this.setState({ height: height - 348 })
}
componentWillMount() {
this.updateDimensions()
}
componentDidMount() {
window.addEventListener("resize", this.updateDimensions.bind(this))
console.log(this.props.context)
}
componentWillUnmount() {
this._isMounted = false;
window.removeEventListener("resize", this.updateDimensions.bind(this))
}
render(){
const {filterPropertiesList, featuredPropertyList} = this.props;
let markers;
if(!filterPropertiesList.isSearched && featuredPropertyList.data.length > 0){
markers = featuredPropertyList.data;
}else if(filterPropertiesList.isSearched && filterPropertiesList.data.properties.length > 0){
markers = filterPropertiesList.data.properties;
}
return (
<Map style={{ height: this.state.height }} center={[25.0657, 55.17128]} zoom={16}>
<LayersControl position="bottomleft">
<BaseLayer checked name="Default">
<TileLayer
attribution='© OpenStreetMap contributors'
url="http://{s}.google.com/vt/lyrs=p&x={x}&y={y}&z={z}"
subdomains={['mt0','mt1','mt2','mt3']}
/>
</BaseLayer>
<BaseLayer name="Satellite">
<TileLayer
attribution='© OpenStreetMap contributors'
url="http://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}"
subdomains={['mt0','mt1','mt2','mt3']}
/>
</BaseLayer>
</LayersControl>
<MarkerClusterGroup iconCreateFunction={createClusterCustomIcon}>
<MyMarkersList markers={markers} />
</MarkerClusterGroup>
</Map>
);
}
}
export default LeafLetMap;
Warning: Failed prop type: The prop to is marked as required in Link, but its value is undefined.
Warning: Failed context type: The context router is marked as
required in Link, but its value is undefined.
I mentioned libraries versions, post container and custom popup where i used react router dom link component.
I also paste occurred warnings as a results.
Related
I'm facing with a problem that I can't get my current location by react-leaflet v4.0.1 and react v18.2.0 when the project has been deployed on IIS.
I Just can get the current location when node js is running on localhost/register but not on domain example.com/register.
maybe its CROS origin problem but I dont know how to handle it by domain
name.
it workes well:
but it has problem with domain:
in Map.js
import React, { useEffect, useState } from 'react'
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet'
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
import icon from './constants'
export default function App() {
function LocationMarker() {
const [position, setPosition] = useState(null)
const [bbox, setBbox] = useState([])
const map = useMap()
useEffect(() => {
map.locate().on('locationfound', function (e) {
setPosition(e.latlng)
map.flyTo(e.latlng, map.getZoom())
setBbox(e.bounds.toBBoxString().split(','))
})
}, [map])
return position === null ? null : (
<Marker position={position} icon={icon}>
<Popup>
You are here. <br />
Map bbox: <br />
<b>Southwest lng</b>: {bbox[0]} <br />
<b>Southwest lat</b>: {bbox[1]} <br />
<b>Northeast lng</b>: {bbox[2]} <br />
<b>Northeast lat</b>: {bbox[3]}
</Popup>
</Marker>
)
}
return (
<MapContainer
center={[35.1951, 51.6068]}
zoom={13}
scrollWheelZoom
style={{ height: '50vh' }}
>
<div style={{ height: '50vh' }}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<LocationMarker />
</div>
</MapContainer>
)
}
**I have problem with react-swipper when i'm trying to separate it into smaller components.
Below is my full code from one component which i want to split from this:
import { Swiper } from 'swiper/react/swiper-react';
import { SwiperSlide } from 'swiper/react/swiper-react';
import { Pagination } from 'swiper';
import 'swiper/swiper.scss';
<div className={styles['categories-showcase']}>
<Swiper
slidesPerView={4}
spaceBetween={200}
grabCursor={true}
pagination={{
clickable: true,
}}
modules={[Pagination]}
>
{categories.map(category => {
return (
<SwiperSlide>
<div className={styles['categories-showcase__item']}>
<img
className={styles['swiper-image']}
src={category.image}
alt={category.name}
/>
</div>
</SwiperSlide>
);
})}
</Swiper>
</div>
into this:
<SwiperSlider>
{categories.map(c => (
<Slide key={c.id} {...c} />
))}
</SwiperSlider>
And here what i've tried:
SwiperSlider - pass multiple slides as children of JSX.Element[]
import React from 'react';
import { Swiper, SwiperSlide } from 'swiper/react/swiper-react';
import { Pagination } from 'swiper';
import 'swiper/swiper.scss';
import styles from './swiper-slider.module.scss';
interface IProps {
children: JSX.Element[];
}
const SwiperSlider = ({ children }: IProps) => {
return (
<div className={styles['showcase']}>
<Swiper
slidesPerView={4}
spaceBetween={200}
grabCursor={true}
pagination={{
clickable: true,
}}
modules={[Pagination]}
>
{children}
</Swiper>
</div>
);
};
export default SwiperSlider;
Slide which takes category as prop
import { ICategory } from 'models/category';
import React from 'react';
import { SwiperSlide } from 'swiper/react/swiper-react';
import 'swiper/swiper.scss';
import styles from './slide.module.scss';
const Slide = (category: ICategory) => {
return (
<SwiperSlide>
<div className={styles['showcase-item']}>
<img className={styles['swiper-image']} src={category.image} alt={category.name} />
</div>
</SwiperSlide>
);
};
export default Slide;
When i'm doing it this way, then my swiper doesn't work properly. It is displayed in one vertical row, when it should be displayed horizontally.
Could you tell me what i'm doing wrong here?
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/kMTMq.png**
I think you should iterate over your array on SwiperSlider and render your custom Slide which export SwiperSlide
const SwiperSlider = ({ children }: IProps) => (
<div className={styles['showcase']}>
<Swiper
slidesPerView={4}
spaceBetween={200}
grabCursor={true}
pagination={{
clickable: true,
}}
modules={[Pagination]}
>
{categories.map((category) => <Slide category={category} /> )}
</Swiper>
</div>
);
export default SwiperSlider;
I'm trying to link the list with the leaflet map. I want to click on a result card and see the marker on the map. I'm using fake data for now in json format. I would like a display like airbnb, list plus display the list on map.
This is the map file :
import React, { useState } from "react";
import { Map, TileLayer, Marker, Popup } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import { Icon } from "leaflet";
import FakeData from "../../../../../../data";
import {Div, PopContainer} from "./style"
const MarkerIcon = new Icon({
iconUrl: "../public/markerIcon.png",
iconSize: [50, 50],
});
function MapBox() {
const [nurse, setNurse] = useState(null);
const bull = <span>•</span>;
return (
<Div>
<Map center={[50.6365654, 3.0635282]} zoom={13} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png"
/>
{FakeData.map((data) => (
<Marker
key={data.id}
icon={MarkerIcon}
position={[data.coordonatelat, data.coordonatelng]}
onmouseover={() => {
setNurse(data);
}}
/>
))}
{nurse && (
<Popup
position={[nurse.coordonatelat, nurse.coordonatelng]}
onClose={() => {
setNurse(null);
}}
>
<PopContainer>
<h2>
{bull}
{nurse.first_name} {nurse.last_name}
{bull}
</h2>
<h3>
{nurse.job}
</h3>
<p> {nurse.address}
<br />
{nurse.phone_number}
</p>
</PopContainer>
</Popup>
)}
</Map>
</Div>
);
}
export default MapBox;
This is the resultPage :
import React from "react";
import FakeData from "../../../../../../data";
import MapBox from "../map/index";
import { Scrollbars } from "react-custom-scrollbars";
import {
Container,
H1,
Card,
CardContent,
H2,
Address,
Phone,
NavLink,
CardActions,
Job,
MapContainer,
BigContainer,
CardContainer,
Box,
Li,
} from "./resultPageStyle";
function Result(props) {
const bull = <span>•</span>;
return (
<BigContainer>
<Container>
<H1>Résultats</H1>
<Box>
<Scrollbars style={{ width: 650, height: 580 }}>
<CardContainer>
{FakeData.slice(0, 30).map((item) => (
<Li key={item.id}>
<Card>
<CardContent>
<Job>{item.job}</Job>
<H2>
{bull}
{item.first_name} {item.last_name}
{bull}
</H2>
<Address>{item.address}</Address>
<Phone>{item.phone_number}</Phone>
</CardContent>
<CardActions>
<NavLink to={"/detailsPage"}>Plus d'infos</NavLink>
</CardActions>
</Card>
</Li>
))}
</CardContainer>
</Scrollbars>
<MapContainer>
<MapBox />
</MapContainer>
</Box>
</Container>
</BigContainer>
);
}
export default Result;
I tried diferent thing but it is not working.
Create a state variable on Result comp to keep track of the card item that holds the lat lng info
Pass it as a prop to Mapbox comp
On the Mapbox comp create a local variable to save the map instance and use it to change the map view every time you click on a card.
divider
function Result() {
const bull = <span>•</span>;
const [cardItem, setCardItem] = useState(null);
const handleCardClick = (item) => {
console.log(item);
setCardItem(item);
};
...
<Card onClick={() => handleCardClick(item)}>
...
<MapContainer>
<MapBox cardItem={cardItem} />
</MapContainer>
}
divider
function MapBox({ cardItem }) {
const [nurse, setNurse] = useState(null);
const bull = <span>•</span>;
const [map, setMap] = useState(null);
useEffect(() => {
if (!cardItem || !map) return;
const { coordonatelat, coordonatelng } = cardItem;
map.setView([coordonatelat, coordonatelng], 13);
}, [cardItem, map]);
...
}
I fixed also your demo issues and added a cursor when you hover on each card.
You can use also map.flyTo which gives a nice animation out of the box instead of map.setView. It's up to you.
Demo
So basically I was able to get a database which stores points in it.
Now I was able to push each point separately to make this database. Now I need to display these points on the map. This is my code so far:
App.js
import './App.css';
import * as React from "react";
import firebase from "./firebase";
import { ChakraProvider } from "#chakra-ui/react";
import { MapContainer, TileLayer, Marker, Popup, useMapEvents, useMap } from 'react-leaflet'
import 'leaflet/dist/leaflet.css'
import * as L from 'leaflet';
import SearchBar from './SearchBar';
import CovidPoint from './CovidPoint';
import LocationMarkers from './LocationMarkers';
class App extends React.Component {
constructor(props){
super(props)
this.state = {
map: null,
points: [<CovidPoint
position={[43.653226, -79.3831843]}
name="point1"
information="random point"
input = {false}
></CovidPoint>]
}
}
changePos = (pos, zoom) => {
const {map} = this.state;
if (map) map.flyTo(pos, zoom);
}
fetchPoints = (newPoints) => {
// fetch info from database
const ref = firebase.database().ref("points")
const pointsref = ref.push()
pointsref.set(newPoints)
this.setState({points: newPoints})
}
componentDidMount() {
const ref = firebase.database().ref("points")
ref.on("value", snapshot => {
console.log("FireB ",snapshot)
if (snapshot && snapshot.exists()) {
this.setState({points: snapshot.val})
}})
console.log("page loaded!")
}
render() {
return (
<div className="App">
<div id="title">
<h1>CovidStopSpots</h1>
<p>A responsive tracker for Covid-19.</p>
</div>
<div id="map">
<MapContainer
id="1"
center={[43.653226, -79.3831843]}
zoom={13}
scrollWheelZoom={false}
whenCreated={(map) => this.setState({ map })}
style={{ height: "100vh " }}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{this.state.points.length > 0 && this.state.points.map(
(point, index) => {
<li key={index}></li>
return point
}) }
{/* <CovidPoint
position={[43.653226, -79.3831843]}
name="point1"
information="random point"
></CovidPoint>
<CovidPoint
position={[50.653226, -79.3831843]}
name="point2"
information="random point"
></CovidPoint> */}
<LocationMarkers points={this.state.points} fetchPoints={this.fetchPoints}></LocationMarkers>
<div id="searchbar">
<SearchBar changePos={this.changePos}/>
</div>
</MapContainer>
</div>
</div>
);
}
}
export default App;
The location Marker file which basically "plots" the points and pushes the to the database.
LocationMarker.js
import { useState } from "react";
import { useMapEvents } from "react-leaflet";
import CovidPoint from "./CovidPoint";
function LocationMarkers(props) {
const [position, setPosition] = useState([]);
useMapEvents({
dblclick(ev) {
console.log("double clicked");
const { lat, lng } = ev.latlng;
setPosition([lat, lng]);
const newPoints = [...props.points];
console.log(newPoints);
newPoints.push(<CovidPoint
position={[lat, lng]}
name="test"
information="test"
input = {true}
></CovidPoint>);
props.fetchPoints(newPoints);
}
});
return null;
}
export default LocationMarkers;
As you can see on this example from documentation, you should use Marker to place a marker. Assuming that you have an array of points (list of coordinates), you can place a list of points like this:
const position = [
[51.505, -0.09],
[51.510, -0.09],
[51.515, -0.09]
]
render(
<MapContainer center={position} zoom={13} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{points?.map((point)=>(
<Marker position={point}>
)}
</Marker>
</MapContainer>,
)
I don't know what is this CovidPoint. But I think you dont need it.
I'm working on a slider of items similar to Netflix but with movies, using the info recovered from the tmdb API.
I would like to add a skeleton load for each dynamically generated item using map to improve the user experience.
First of all for the skeleton loading I got a lot of inspiration from this code available on pen code that I adapted afterwards.
https://codepen.io/mxbck/pen/EvmLVp
I try a solution first here but it does not work, I voluntarily remove code that did not relate to my problem for clarity:
Slider component
import React, {PureComponent , Component } from "react";
import style from './Caroussel.css';
import MovieItem from "../components/MovieItem";
import MovieItemContainer from '../components/MovieItem/MovieItemContainer';
class Slider extends Component {
constructor(props){
super(props);
this.handleOnLeftArrowClick = this.leftArrowClick.bind(this);
this.handleOnRightArrowClick = this.rightArrowClick.bind(this);
this.state = {
sliderItems: [],
}
}
componentDidMount() {
this.updateSliderState();
this.setState({
totalItems: this.props.movieList.length,
sliderItem: this.props.movieList
})
}
componentWillMount(){
if(typeof(window) !== 'undefined') {
$(window).on('resize', this.updateSliderState.bind(this))
}
}
render(){
const { sliderItem} = this.state;
const sliderClass = cx ({
sliderMask:true,
moving
})
return(
<div className="wrapper">
<div className={style.slider}>
<div className={sliderClass} ref="slider">
{this.state.sliderItem ?
sliderItem.map((element, index) => (
<MovieItemContainer>
<MovieItem
key={index}
title={element.title}
id={element.id}
release_date={element.release_date}
url={element.backdrop_path}
/>
</MovieItemContainer>
))
:
sliderItem.map((element, index) => {
null
})
}
</div>
{
click &&
<div className={style.leftArrow} ref="leftArrow">
<IosArrowBack onClick{this.handleOnLeftArrowClick} color="black" />
</div>
}
<div className={style.rightArrow} ref="rightArrow">
<IosArrowForward onClick={this.handleOnRightArrowClick} color="black" />
</div>
</div>
</div>
);
}
}
export default Slider;
MovieItem Container component
import React, {Component} from "react";
import '../../../style/card.scss';
class MovieItemContainer extends React.Component {
render() {
return (
<div className="card">
{this.props.children}
</div>
);
}
}
export default MovieItemContainer;
MovieItem component
import React from 'react';
import Moment from 'react-moment';
import style from './MovieItem.css';
import {Link} from 'react-router';
const MovieItem = ({ url, title, release_date, id }) => {
let link ='https://image.tmdb.org/t/p/w300/'+url;
const text_truncate = (str, length, ending) => {
if (length == null) {
length = 100;
}
if (ending == null) {
ending = '...';
}
if (str.length > length) {
return str.substring(0, length - ending.length) + ending;
} else {
return str;
}
};
return (
<div className={style.sliderItem}>
<div style={{ borderBottomLeftRadius: 8, borderBottomRightRadius:8 }} className={style.sliderItemInner}>
<img style={{ borderRadius: 8 }} className={style.cover} src={link} />
<div className={style.shadow}></div>
<div className={style.titles}>
<span className={style.title}>
<Link className={style.title} to={`film/${id}`}>
{text_truncate(title,18)}
</Link>
</span>
<span className={style.release_date}>
<Moment format="YYYY">
{release_date}
</Moment>
</span>
</div>
</div>
</div>
)
}
export default MovieItem;
The MovieItem component only makes the item a movie with a background image, the title of the movie and the year of release.
In the example above, the loading is done for only one card, I would just adapt this code for each of the generated items (10 items).
I thank you in advance for your help and your answers.