Changing color on layer React-Mapbox-gl - reactjs

I am new to React-mapbox GL. I have tried for a while now to look at the examples but can't figure out how to change the layer's color on enter/hover. I have 2 questions so far.
map.on('mouseenter', 'clusters', () => {
map.getCanvas().style.cursor = 'pointer';
});
How can I define the the cluster element for each function in Reactmapbox gl? I don't quite understand how the interactiveLayerIds works I suppose?
question 2.
const onMouseEnter = useCallback(event =>{
if (event.features[0].layer.id==="unclustered-point"){
/* console.log(event.features[0].layer.paint.'circle-color') */
}
})
I have attempted this so far(the whole code is below) but it tells me that circle-color is a unexpected token. OnEnter this unclustered-point layer I want to change the color of the element so the user can clearly see what element they are hovering over? How would I go about doing this in React mapbox gl if I cant change the circle color?
THE WHOLE CODE:
import React, { useContext, useEffect, useRef,useState,useCallback } from 'react';
import './MapViews.css';
import { useNavigate } from 'react-router-dom';
import ReactMapGL, { Marker, Layer, Source } from 'react-map-gl';
import SourceFinder from '../../Apis/SourceFinder';
import { SourceContext } from '../../context/SourceContext';
import { clusterLayer, clusterCountLayer, unclusteredPointLayer } from './Layers';
const MapView = () => {
const navigate = useNavigate()
const { sources, setSources } = useContext(SourceContext)
const [viewport, setViewport] = React.useState({
longitude: 10.757933,
latitude: 59.91149,
zoom: 12,
bearing: 0,
pitch: 0
});
const mapRef = useRef(null);
function getCursor({isHovering, isDragging}) {
return isDragging ? 'grabbing' : isHovering ? 'pointer' : 'default';
}
useEffect(() => {
const fetchData = async () => {
try {
const response = await SourceFinder.get("/sources");
setSources(response.data.data.plass);
} catch (error) { }
};
fetchData();
}, [])
const onMouseEnter = useCallback(event =>{
if (event.features[0].layer.id==="unclustered-point"){
/* console.log(event.features[0].layer.paint.'circle-color') */
}
})
const ShowMore = event => {
if(event.features[0].layer.id==="unclustered-point"){
const feature = event.features[0];
console.log(feature)
mapRef.current.getMap().getCanvas().style.cursor="pointer"
}else{
const feature = event.features[0];
const clusterId = feature.properties.cluster_id;
const mapboxSource = mapRef.current.getMap().getSource('stasjoner');
mapboxSource.getClusterExpansionZoom(clusterId, (err, zoom) => {
if (err) {
return;
}
setViewport({
...viewport,
longitude: feature.geometry.coordinates[0],
latitude: feature.geometry.coordinates[1],
zoom,
transitionDuration: 500
});
});
}
};
return (
<ReactMapGL {...viewport} width="100%" height="100%" getCursor={getCursor} onMouseEnter={onMouseEnter} interactiveLayerIds={[clusterLayer.id,unclusteredPointLayer.id]} mapboxApiAccessToken={"SECRET"} clickRadius={2} onViewportChange={setViewport} mapStyle="mapbox://styles/mapbox/streets-v11" onClick={ShowMore} ref={mapRef}>
<Source id="stasjoner" type="geojson" data={sources} cluster={true} clusterMaxZoom={14} clusterRadius={50} >
<Layer {...clusterLayer} />
<Layer {...clusterCountLayer} />
<Layer {...unclusteredPointLayer}/>
</Source>
</ReactMapGL>
);
};
export default MapView;
LAYERS.JS
//Hvergang vi skal ha 2 eller flere baller
export const clusterLayer = {
id: 'clusters',
type: 'circle',
source: 'stasjoner',
filter: ['has', 'point_count'],
paint: {
'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 500, '#f28cb1'],
'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40],
}
};
//Dette er tallene som er inne i ballene
export const clusterCountLayer = {
id: 'cluster-count',
type: 'symbol',
source: 'stasjoner',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12,
}
};
//Per punkt
export const unclusteredPointLayer = {
id: 'unclustered-point',
type: 'circle',
source: 'stasjoner',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 8,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff',
}
};

Related

Two setState being enacted at the same time updating page separately

I am creating a graph using the react-plotly library. The data from the graph is being queried from an API endpoint.
The service file is shown below
import React from 'react'
export default async function GetStockData(token,ticker,setData, setSuccess) {
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer " + token)
var formdata = new FormData();
formdata.append("Tick", ticker);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: formdata,
redirect: 'follow'
};
await fetch("https://abc.azurewebsites.net/api/stocks", requestOptions)
.then(response => response.json())
.then(response => setData(JSON.parse(response.content))
}
I am still the add error handling. I have fixed the ticker to a value that I know works for now.
This service is then called by the component below:
import React, { useState, useContext, useEffect} from 'react'
import GetStockData from '../Services/GetStockData'
import Plot from 'react-plotly.js';
import { sampleData } from './sampleChartdata';
import AuthContext from "../Store/AuthContext";
export default function CandleStick() {
const authCtx = useContext(AuthContext);
const token = authCtx.token
const [chartData, setChartData] = useState(sampleData);
const [chartSuccess, setChartSuccess] = useState(false)
const [preppedData, setpreppedData] = useState(null);
const [layout, setlayout] = useState(null);
const ticker = 'MSFT'
const clickHandler = async()=>{
await GetStockData(token,ticker,setChartData, setChartSuccess)
const {data, preplayout} = dataPrep()
setpreppedData(data)
setlayout(preplayout)
}
const dataPrep =()=>{
var dateData = []
var closeData = []
var openData = []
var lowData = []
var highData = []
for(var prop in chartData["Time Series (Daily)"]){
dateData.push(prop)
for(var prop2 in chartData["Time Series (Daily)"][prop]){
if (prop2=="1. open"){ openData.push(chartData["Time Series (Daily)"][prop][prop2])}
if (prop2=="2. high"){ highData.push(chartData["Time Series (Daily)"][prop][prop2])}
if (prop2=="3. low"){ lowData.push(chartData["Time Series (Daily)"][prop][prop2])}
if (prop2=="5. adjusted close"){ closeData.push(chartData["Time Series (Daily)"][prop][prop2])}
}
}
var trace1 = {
x: dateData,
close:closeData,
increasing: {line: {color: 'green'}},
decreasing: {line: {color: 'red'}},
high: highData,
line: {color: 'rgba(31,119,180,1)'},
low: lowData,
open: openData,
type: 'candlestick',
xaxis: 'x',
yaxis: 'y'
};
var data = [trace1]
var layout = {
dragmode: 'zoom',
margin: {
r: 10,
t: 25,
b: 40,
l: 60
},
showlegend: false,
xaxis: {
autorange: true,
domain: [0, 1],
title: 'Date',
type: 'date'
},
yaxis: {
autorange: true,
domain: [0, 1],
type: 'linear'
}
};
return {data , layout}
} ;
useEffect(() => {
if (preppedData !== null) setIsDataLoaded(true);console.log(preppedData)
}, [preppedData]);
return (
<>
<div>{isDataLoaded?
<Plot
data={preppedData}
layout={layout}>
</Plot>: null}
</div>
<button onClick={()=>clickHandler()}>Refresh</button>
</>
)
}
There are probably a whole host of things that could be improved here. I am using too stateful variables for a start. I currently have a local store of data to use as a sample to prevent an error occurring prior to the request being made. Any comment on how to manage first render when awaiting web based content would be massively appreciated.
My core question relate to setChartData, setChartSuccess. On the press of the button, these are passed to the service file where they are updated simultaneously. However, the chartSuccess variable seems to update prior to ChartData. The jsx conditional triggers and renders the graph but it doesn't contain the latest preppedData. On pressing the button a second time the updated data appears. Am I making an error in the sequencing?
From what I see, you can do 2 things to solve this. Either set success state inside the click handler and if the success state can't be moved to elsewhere as it might be used in some other place, you can have a new state variable and use it for the condition for render or not.
I'm showing you how to do it with the 2nd method here, but you can give 1st method a try if you don't have problem I mentioned.
So, your component code will look like this.
import React, { useState, useContext, useEffect } from 'react';
import GetStockData from '../Services/GetStockData';
import Plot from 'react-plotly.js';
import { sampleData } from './sampleChartdata';
import AuthContext from '../Store/AuthContext';
export default function CandleStick() {
const authCtx = useContext(AuthContext);
const token = authCtx.token;
const [chartData, setChartData] = useState(sampleData);
const [chartSuccess, setChartSuccess] = useState(false);
const [preppedData, setpreppedData] = useState(null);
const [layout, setlayout] = useState(null);
const [isDataLoaded, setIsDataLoaded] = useState(false);
const ticker = 'MSFT';
const clickHandler = async () => {
await GetStockData(token, ticker, setChartData, setChartSuccess);
const { data, preplayout } = dataPrep();
setpreppedData(data);
setlayout(preplayout);
setIsDataLoaded(true);
};
const dataPrep = () => {
var dateData = [];
var closeData = [];
var openData = [];
var lowData = [];
var highData = [];
for (var prop in chartData['Time Series (Daily)']) {
dateData.push(prop);
for (var prop2 in chartData['Time Series (Daily)'][prop]) {
if (prop2 == '1. open') {
openData.push(chartData['Time Series (Daily)'][prop][prop2]);
}
if (prop2 == '2. high') {
highData.push(chartData['Time Series (Daily)'][prop][prop2]);
}
if (prop2 == '3. low') {
lowData.push(chartData['Time Series (Daily)'][prop][prop2]);
}
if (prop2 == '5. adjusted close') {
closeData.push(chartData['Time Series (Daily)'][prop][prop2]);
}
}
}
var trace1 = {
x: dateData,
close: closeData,
increasing: { line: { color: 'green' } },
decreasing: { line: { color: 'red' } },
high: highData,
line: { color: 'rgba(31,119,180,1)' },
low: lowData,
open: openData,
type: 'candlestick',
xaxis: 'x',
yaxis: 'y',
};
var data = [trace1];
var layout = {
dragmode: 'zoom',
margin: {
r: 10,
t: 25,
b: 40,
l: 60,
},
showlegend: false,
xaxis: {
autorange: true,
domain: [0, 1],
title: 'Date',
type: 'date',
},
yaxis: {
autorange: true,
domain: [0, 1],
type: 'linear',
},
};
return { data, layout };
};
return (
<>
<div>
{isDataLoaded ? <Plot data={preppedData} layout={layout}></Plot> : null}
</div>
<button onClick={() => clickHandler()}>Refresh</button>
</>
);
}
UPDATED as per requirement
import React, { useState, useContext, useEffect } from 'react';
import GetStockData from '../Services/GetStockData';
import Plot from 'react-plotly.js';
import { sampleData } from './sampleChartdata';
import AuthContext from '../Store/AuthContext';
export default function CandleStick() {
const authCtx = useContext(AuthContext);
const token = authCtx.token;
const [chartData, setChartData] = useState(sampleData);
const [chartSuccess, setChartSuccess] = useState(false);
const [preppedData, setpreppedData] = useState(null);
const [layout, setlayout] = useState(null);
const [isDataLoaded, setIsDataLoaded] = useState(false);
const ticker = 'MSFT';
const clickHandler = async () => {
await GetStockData(token, ticker, setChartData, setChartSuccess);
const { data, preplayout } = dataPrep();
setpreppedData(data);
setlayout(preplayout);
};
const dataPrep = () => {
var dateData = [];
var closeData = [];
var openData = [];
var lowData = [];
var highData = [];
for (var prop in chartData['Time Series (Daily)']) {
dateData.push(prop);
for (var prop2 in chartData['Time Series (Daily)'][prop]) {
if (prop2 == '1. open') {
openData.push(chartData['Time Series (Daily)'][prop][prop2]);
}
if (prop2 == '2. high') {
highData.push(chartData['Time Series (Daily)'][prop][prop2]);
}
if (prop2 == '3. low') {
lowData.push(chartData['Time Series (Daily)'][prop][prop2]);
}
if (prop2 == '5. adjusted close') {
closeData.push(chartData['Time Series (Daily)'][prop][prop2]);
}
}
}
var trace1 = {
x: dateData,
close: closeData,
increasing: { line: { color: 'green' } },
decreasing: { line: { color: 'red' } },
high: highData,
line: { color: 'rgba(31,119,180,1)' },
low: lowData,
open: openData,
type: 'candlestick',
xaxis: 'x',
yaxis: 'y',
};
var data = [trace1];
var layout = {
dragmode: 'zoom',
margin: {
r: 10,
t: 25,
b: 40,
l: 60,
},
showlegend: false,
xaxis: {
autorange: true,
domain: [0, 1],
title: 'Date',
type: 'date',
},
yaxis: {
autorange: true,
domain: [0, 1],
type: 'linear',
},
};
return { data, layout };
};
useEffect(() => {
if (preppedData !== null) setIsDataLoaded(true);
}, [preppedData]);
return (
<>
<div>
{isDataLoaded ? <Plot data={preppedData} layout={layout}></Plot> : null}
</div>
<button onClick={() => clickHandler()}>Refresh</button>
</>
);
}

React-map-gl cluster array is empty

I'm using the user-supercluster library to portray clusters on my map. I've upgraded React-map-gl to the newest version. The map shows up but I cannot get my clusters or markers to show up. When I console the cluster array it's completely empty.
When I console log the points, the data is there. I'm not sure what is the issue.
import React, { useRef } from "react";
import Map, {
Marker,
FullscreenControl,
NavigationControl,
} from "react-map-gl";
import HotelsQueryContext from "#Src/routes/Hotels/HotelsQueryContext";
import { useSelector } from "react-redux";
import { hotelsSelectors } from "#Src/redux/rootSelectors";
import useSupercluster from "use-supercluster";
import HotelMarker from "./HotelMarker";
import HotelCardWeb from "../../Web/components/HotelCardWeb";
import HotelCardPhone from "../../Phone/components/HotelCardPhone";
import "mapbox-gl/dist/mapbox-gl.css";
import "./Map.scss";
function HotelMap() {
const mapRef = useRef();
const hotelsQuery = React.useContext(HotelsQueryContext);
const filteredHotels = useSelector(hotelsSelectors.getHotelsFilteredList);
const [viewport, setViewport] = React.useState({
latitude: parseFloat(hotelsQuery.lat),
longitude: parseFloat(hotelsQuery.lon),
zoom: 11.3,
});
const [selectedHotel, setSelectedHotel] = React.useState(null);
//Covert filtered hotels to geojson data objects
const points = filteredHotels.map((hotel) => ({
type: "Feature",
properties: {
cluster: false,
hotel_id: hotel.access_hotel_id,
category: hotel.name,
},
geometry: {
type: "Point",
coordinates: [hotel.location.lon, hotel.location.lat],
},
}));
const bounds = mapRef.current
? mapRef.current.getMap().getBounds().toArray().flat()
: null;
const { clusters, supercluster } = useSupercluster({
points,
bounds,
zoom: viewport.zoom,
options: { radius: 100, maxZoom: 20, minPoints: 9 },
});
//Handles zoom when user clicks on cluster
const clusterZoom = (clusterID, latitude, longitude) => {
mapRef.current?.flyTo({ cener: [longitude, latitude], duration: 2000 });
const expansionZoom = Math.min(
supercluster.getClusterExpansionZoom(clusterID),
20
);
setViewport({
...viewport,
latitude,
longitude,
zoom: expansionZoom,
transitionInterpolator: new FlyToInterpolator({
speed: 2,
}),
transitionDuration: "auto",
});
};
console.log({ clusters });
return (
<div className="searchResults--map-wrapper">
<div className="Map">
<Map
initialViewState={{
...viewport,
}}
mapStyle={process.env.REACT_APP_MAPBOX_STYLE_URL}
mapboxAccessToken={process.env.REACT_APP_MAPBOX_ACCESS_TOKEN}
onMove={(e) => setViewport(e.viewState)}
onClick={() => setSelectedHotel(null)}
style={{
width: "100%",
height: "100%",
}}
ref={mapRef}
>
{clusters.map((cluster, idx) => {
const hotel = filteredHotels.find(
(el) => el.access_hotel_id === cluster.properties.hotel_id
);
const [longitude, latitude] = cluster.geometry.coordinates;
const { cluster: isCluster, point_count } = cluster.properties;
if (isCluster) {
return (
<Marker
key={cluster.id}
latitude={latitude}
longitude={longitude}
>
<div
className="Map-clusterMarker"
onClick={() => clusterZoom(cluster.id, latitude, longitude)}
>
{point_count} hotels
</div>
</Marker>
);
}
return (
hotel && (
<HotelMarker
key={hotel.access_hotel_id}
hotel={hotel}
selectedHotel={selectedHotel}
setSelectedHotel={setSelectedHotel}
/>
)
);
})}
<div className="Map-fullScreenCtrl">
<FullscreenControl />
</div>
<div className="Map-navigationCtrl">
<NavigationControl showCompass={false} />
</div>
</Map>
</div>
</div>
);
}
export default HotelMap;
```React

Real-time Location Functionality is not working. `animateMarkerToCoordinate` function is not Moving to the Specified Coordinates

I'm working on a project and I need to show the real-time location of the user on the map. Below is my code for this functionality but that is not working as I expected. It looks like animateMarkerToCoordinate function is not working properly(not moving the marker to the specified coordinartes) but I don't know surely. Can anyone please help me regarding this? Thank you.
import { StyleSheet, View, Dimensions, Platform } from "react-native";
import React, { useEffect, useState, useRef } from "react";
import MapView, { AnimatedRegion, MarkerAnimated } from "react-native-maps";
import Header from "../components/Header";
import ErrorDialog from "../components/ErrorDialog";
import * as Location from "expo-location";
const { width, height } = Dimensions.get("window");
const ASPECT_RATIO = width / height;
const LATITUDE = 30.3753;
const LONGITUDE = 69.3451;
const LATITUDE_DELTA = 0.005;
const LONGITUDE_DELTA = 0.005;
const LiveLocation = ({ navigation }) => {
const markerRef = useRef(null);
const [error, setError] = useState("");
const [latlng, setLatlng] = useState({ lat: LATITUDE, lng: LONGITUDE });
const [coordinates, setCoordinates] = useState(
new AnimatedRegion({
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: 0,
longitudeDelta: 0,
})
);
const getMapRegion = () => ({
latitude: latlng.lat,
longitude: latlng.lng,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
});
useEffect(() => {
let unsubscribe;
Location.requestForegroundPermissionsAsync().then((response) => {
if (response.status === "granted") {
Location?.watchPositionAsync(
{
accuracy: Location.Accuracy.BestForNavigation,
timeInterval: 2000,
},
(location_update) => {
const duration = 500;
const { latitude, longitude } = location_update.coords;
const newCoordinates = {
latitude,
longitude,
};
if (Platform.OS === "android") {
if (markerRef) {
markerRef.current.animateMarkerToCoordinate(
newCoordinates,
duration
);
}
}
setLatlng({ lat: latitude, lng: longitude });
}
)
.then((unsub) => {
unsubscribe = unsub;
})
.catch((error) => {
console.log(error.message);
});
} else {
setError("Permission to access location was denied");
}
});
return () => {
unsubscribe.remove();
};
}, []);
return (
<View style={[StyleSheet.absoluteFillObject, styles.container]}>
<MapView
style={[StyleSheet.absoluteFillObject, styles.mapView]}
loadingEnabled
region={getMapRegion()}
>
<MarkerAnimated ref={markerRef} coordinate={coordinates} />
</MapView>
<View style={styles.heading}>
<Header text="Live Location" navigation={() => navigation.goBack()} />
</View>
<View>
<ErrorDialog
visible={!!error}
errorHeader={"Error!"}
errorDescription={error}
clearError={() => {
setError("");
}}
/>
</View>
</View>
);
};
export default LiveLocation;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignContent: "center",
},
mapView: {
flex: 1,
},
heading: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0 },
});

How can I display an array of images after get the urls React Native

Im trying to display a preview of the picked images after pick them, im using this library import { AssetsSelector } from 'expo-images-picker';
This is the code to pick the image:
import React, { useMemo } from 'react';
import { Text, View, StyleSheet, SafeAreaView, Alert } from 'react-native';
import { AssetsSelector } from 'expo-images-picker';
import { Ionicons } from '#expo/vector-icons';
import { AntDesign } from '#expo/vector-icons';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { MediaType } from 'expo-media-library';
import { useNavigation } from '#react-navigation/core';
export default function App() {
const navigation = useNavigation();
const onSuccess = (data: any) => {
const filteredUri = data.filter(({ uri }) => uri).map(({ uri }) => uri);
navigation.navigate('AddProductScreen',
{
filteredUri: filteredUri,
});
};
const widgetErrors = useMemo(
() => ({
errorTextColor: 'black',
errorMessages: {
hasErrorWithPermissions: 'Please Allow media gallery permissions.',
hasErrorWithLoading: 'There was error while loading images.',
hasErrorWithResizing: 'There was error while loading images.',
hasNoAssets: 'No images found.',
},
}),
[]
);
const widgetSettings = useMemo(
() => ({
getImageMetaData: false,
initialLoad: 100,
assetsType: [MediaType.photo, MediaType.video],
minSelection: 1,
maxSelection: 3,
portraitCols: 4,
landscapeCols: 4,
}),
[]
);
const widgetResize = useMemo(
() => ({
width: 50,
compress: 0.7,
base64: false,
saveTo: 'jpeg',
}),
[]
);
const _textStyle = {
color: 'white',
};
const _buttonStyle = {
backgroundColor: 'orange',
borderRadius: 5,
};
const widgetNavigator = useMemo(
() => ({
Texts: {
finish: 'finish',
back: 'back',
selected: 'selected',
},
midTextColor: 'black',
minSelection: 1,
buttonTextStyle: _textStyle,
buttonStyle: _buttonStyle,
onBack: () => {navigation.goBack()},
onSuccess: (e: any) => onSuccess(e),
}),
[]
);
const widgetStyles = useMemo(
() => ({
margin: 2,
bgColor: 'white',
spinnerColor: 'blue',
widgetWidth: 99,
videoIcon: {
Component: Ionicons,
iconName: 'ios-videocam',
color: 'tomato',
size: 20,
},
selectedIcon: {
Component: Ionicons,
iconName: 'ios-checkmark-circle-outline',
color: 'white',
bg: '#0eb14970',
size: 26,
},
}),
[]
);
return (
<SafeAreaProvider>
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<AssetsSelector
Settings={widgetSettings}
Errors={widgetErrors}
Styles={widgetStyles}
Navigator={widgetNavigator}
/>
</View>
</SafeAreaView>
</SafeAreaProvider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
This is the code where I want o display the images, im using react navigation props to get the array:
const showPickedImages = ({ route, navigations }) => {
const navigation = useNavigation();
var filteredUri = route.params?.filteredUri;
return(
<View>
//Here I want to show the preview of the picked images
<View/>
)}
You can use Flatlist or ScrollView for this.
<Flatlist
ListEmptyComponent={
<Text>Loading...</Text>} // show loading text until you get the data
data={filteredUri}
renderItem={(uri)=>
<Image source={{uri}} style={{widht:100, height:100}} />
}
/>

How to place my dispatch function with React and React Leaflet for return coords lat + lng

I have an input (on children component) that return coordinates by props (text):
import React, { useEffect, useState, useRef, useMemo } from 'react'
import { useEnderecoValue } from '../../contexts/EnderecoContext'
import 'leaflet/dist/leaflet.css'
import Leaflet from 'leaflet'
import { MapContainer, Marker, useMap, TileLayer, Popup } from 'react-leaflet'
export default function App(text: any) {
const [lat, setLat] = useState(48.856614)
const [lng, setLng] = useState(2.3522219)
const [state, dispatch] = useEnderecoValue()
const icon = new Leaflet.DivIcon({
className: 'custom-div-icon',
html:
"<div style='background-color:#c30b82;' class='marker-pin'></div><i class='material-icons'><img src='img/marker-icon.png'></i>",
iconSize: [30, 42],
iconAnchor: [15, 42],
popupAnchor: [-3, -42]
})
useEffect(() => {
if (text.text) {
setLat(text.text.features[0].geometry.coordinates[1])
setLng(text.text.features[0].geometry.coordinates[0])
}
}, [text])
function SetViewOnClick({ coords }: any) {
const map = useMap()
map.flyTo(coords, map.getZoom())
return null
}
My Marker is draggable and the popup display address and coords if I search address on input, or if the Marker is dragded:
const markerRef = useRef(null)
const eventHandlers = useMemo(
() => ({
dragend() {
const marker = markerRef.current
if (marker != null) {
const { lat, lng } = marker.getLatLng()
setLat(lat)
setLng(lng)
}
}
}),
[]
)
const popup = () => {
if (text.text) {
return text.text.query + ' ' + `lat: ${lat}, long: ${lng}`
}
return (
"Address by default" +
' ' +
`lat: ${lat}, long: ${lng}`
)
}
return (
<MapContainer
center={[lat, lng]}
attributionControl={false}
zoomControl={false}
zoom={18}
style={{
height: '350px',
position: 'relative',
outline: 'none',
maxWidth: '696px',
display: 'block',
margin: '15px auto',
width: '100%'
}}
>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<Marker
position={[lat, lng]}
icon={icon}
draggable={true}
ref={markerRef}
eventHandlers={eventHandlers}
>
<Popup>
<span>{popup()}</span>
</Popup>
<SetViewOnClick coords={[lat, lng]} />
</Marker>
</MapContainer>
)
}
How to place my dispatch function for return coords when I search address and whenthe Marker is dragged ? (just when the value change)
dispatch({
type: 'SET_COORDS',
latitude: lat,
longitude: lng
})
On your Search comp place dispatch inside searchLocation to be able to change lat long.
const searchLocation = async () => {
fetch(
"https://api-adresse.data.gouv.fr/search?" +
new URLSearchParams({
q: state.location,
})
)
.then((data) => data.json())
.then((text) => {
setResp({ text });
dispatch({
type: "SET_COORDS",
latitude: text.features[0].geometry.coordinates[1],
longitude: text.features[0].geometry.coordinates[0],
});
})
.catch(function (error) {
console.log("request failed", error);
});
};
Replace <Map {...resp} /> with <Map text={resp} /> because it causes multiple rerenders and the page becomes unresponsive. Not sure what you were trying to do.
Demo

Resources