in the tomtom docs i find many handlers, but there are no given examples on how to use them, especially in react..say i have this very basic tomtom map component, and i want to handle a user click event to drop a pin/marker on the clicked location..how can i handle the map click event? in react syntax
function MapLocation() {
const mapElement = useRef();
const [mapLongitude, setMapLongitude] = useState(-121.91599);
const [mapLatitude, setMapLatitude] = useState(37.36765);
const [mapZoom, setMapZoom] = useState(13);
const [map, setMap] = useState({});
useEffect(() => {
let map = tt.map({
key: "key here",
container: mapElement.current,
center: [mapLongitude, mapLatitude],
zoom: mapZoom
});
setMap(map);
var mcoords = [-121.91595, 37.36729];
var marker = new tt.Marker().setLngLat(mcoords).addTo(map);
return () => map.remove();
}, []);
return (<div ref={mapElement} className="mapDiv h-100"></div>)
}
Related
I am quite new to Arcgis-JS and React. As suggested here, I am using a functional component with the useEffect hook to integrate my map.
Now I want to display a line within my map when I click on a certain row of a list. On click I am fetching the appropriate coordinates to be displayed and storing them to a context-variable (dbPageContext.currentGeom).
The problem: When I want to display another line, the entire map-component has to re-render as I am passing the line-array-variable as a second argument to the useEffect hook.
const MapComp = () => {
const mapRef = useRef();
const dbPageContext = useContext(DbPageContext);
useEffect(() => {
const mainMap = new Map({
layers: [layer],
basemap: "arcgis-topographic", // Basemap layer service
});
const graphicsLayer = new GraphicsLayer();
mainMap.add(graphicsLayer);
const polyline = {
type: "polyline",
paths: dbPageContext.currentGeom,
};
const simpleLineSymbol = {
type: "simple-line",
color: [0, 230, 250],
width: 4,
};
const polylineGraphic = new Graphic({
geometry: polyline,
symbol: simpleLineSymbol,
});
graphicsLayer.add(polylineGraphic);
const view = new MapView({
container: mapRef.current,
map: mainMap,
spatialReference: {
wkid: 3857,
},
});
return () => {
view && view.destroy();
};
}, [dbPageContext.currentGeom]);
return (
<div>
<div className="webmap" ref={mapRef} />
</div>
);
};
export default MapComp;
How can I update only the graphics-layer without updating the entire map-component? Would be great if someone could help me finding a solution for that.
EDIT: I also tried to implement the map without using the useeffect hook. But then, nothing was displayed.
You need to separate the effects. On page load, you should have one effect that creates the map. Then a second effect can update the map when dbPageContext.currentGeom changes.
const MapComp = () => {
const mapRef = useRef();
const dbPageContext = useContext(DbPageContext);
// Memoize this, as you only need to create it once, but you also need
// it to be available within scope of both of the following useEffects
const graphicsLayer = React.useMemo(
() => new GraphicsLayer(),
[]
);
// On mount, create the map, view, and teardown
useEffect(() => {
const mainMap = new Map({
layers: [layer],
basemap: "arcgis-topographic", // Basemap layer service
});
const view = new MapView({
container: mapRef.current,
map: mainMap,
spatialReference: {
wkid: 3857,
},
});
mainMap.add(graphicsLayer);
return () => {
view && view.destroy();
};
}, [])
// When dbPageContext.currentGeom changes, add a polyline
// to the graphics layer
useEffect(() => {
const polyline = {
type: "polyline",
paths: dbPageContext.currentGeom,
};
const simpleLineSymbol = {
type: "simple-line",
color: [0, 230, 250],
width: 4,
};
const polylineGraphic = new Graphic({
geometry: polyline,
symbol: simpleLineSymbol,
});
// Clear previously added lines (if that's what you want)
graphicsLayer.removeAll()
graphicsLayer.add(polylineGraphic);
}, [dbPageContext.currentGeom]);
return (
<div>
<div className="webmap" ref={mapRef} />
</div>
);
};
export default MapComp;
I 'm working with a React Leaflet map that uses state variables to set properties of the map. I tried the following in my code to allow for a min and max zoom when someone uses the scrollwheel. Currently it still does not enable the min and max zoom. Any suggestions on why it's not working? Thank you!
export default function Home () {
//creating use state variables
const [center, setCenter] = useState([43.88, -72.7317])
const [zoom, setZoom] = useState(8)
const [minZoom, setMinZoom] = useState(5)
const [maxZoom, setMaxZoom] = useState(15)
const [featuredDisplay, setFeaturedDisplay] = useState(true)
const [countyStoryDisplay, setCountyStoryDisplay] = useState(false)
const [selectedCounty, setSelectedCounty] = useState('')
const [shuffledIndex, setShuffledIndex] = useState(0)
const [impact, setImpact] = useState('')
const [navCountySelect, setNavCountySelect] = useState('')
const [mobileView, setMobileView] = useState(false)
const [isSelected, setIsSelected] = useState(false)
//creating a material ui style for maroon text
const MaroonTextTypography = withStyles({
root: {
color: '#5a203c'
}
})(Typography)
//useEffect to set the mobile view for navigation based on the window width
useEffect(() => {
const setResponsiveness = () => {
return window.innerWidth < 900
? setMobileView(true)
: setMobileView(false)
}
setResponsiveness()
window.addEventListener('resize', () => setResponsiveness())
return () => {
window.removeEventListener('resize', () => setResponsiveness())
}
}, [])
return (
<>
//map component
<Map
navCountySelect={navCountySelect}
isSelected={isSelected}
setIsSelected={setIsSelected}
center={center}
setCenter={setCenter}
zoom={zoom}
setZoom={setZoom}
setMinZoom={setMinZoom}
setMaxZoom={setMaxZoom}
setFeaturedDisplay={setFeaturedDisplay}
setSelectedCounty={setSelectedCounty}
setCountyStoryDisplay={setCountyStoryDisplay}
countyStoryDisplay={countyStoryDisplay}
setShuffledIndex={setShuffledIndex}
setImpact={setImpact}
setNavCountySelect={setNavCountySelect}
/>
Quick thing you could try is to change to this:
setMinZoom={minZoom}
setMaxZoom={maxZoom}
Since now are you just passing the function rather than the number?
I am currently working with react leaflet maps, are you using the react-leaftlet package?
Since it does not have a Mapcomponent. Could you share the imports?
Would also recommend not using state for constants since you can pass those directly to the component if you do not intend to change them.
I want to get data from https://finnhub.io/docs/api/websocket-trades.
console.log is working, but I can't get it rendered to the screen.
Here are the code for example:
// function to get websocket's data
const streamingStockPrice = (symbol) => {
const socket = new WebSocket('wss://ws.finnhub.io?token=c7d2eiqad3idhma6grrg');
// Connection opened -> Subscribe
socket.addEventListener("open", function (event) {
socket.send(JSON.stringify({ type: "subscribe", symbol }));
});
// Listen for messages
const stockPrice = socket.addEventListener("message", function (event) {
const str = event.data;
const currentStockPrice = str.substring(
str.indexOf(`"p"`) + 4,
str.indexOf(`,"s"`)
);
console.log(
"Message from server ",
str.substring(str.indexOf(`"p"`) + 4, str.indexOf(`,"s"`))
);
return currentStockPrice;
});
// Unsubscribe
var unsubscribe = function (symbol) {
socket.send(JSON.stringify({ type: "unsubscribe", symbol: symbol }));
};
return stockPrice;
};
export default streamingStockPrice;
// render to screen
import streamingStockPrice from "./streamingStockPrice";
const toScreen = () => {
const [stockPrice, setStockPrice] = useState("");
useEffect(() => {
setStockPrice(streamingStockPrice("GME"));
}, [stockPrice]);
return (
<div>
<h4>${stockPrice}</h4>
</div>
);
};
Please help. I appreciate it!
First I want to warn you, this bit of code will create an infinite loop. What you are saying here is, if the stockPrice changes than set the stockPrice.
const [stockPrice, setStockPrice] = useState(""); useEffect(() => { setStockPrice(streamingStockPrice("GME")); }, [stockPrice]);
Now that's out of the way, since you are using react you should create a component for displaying the stockPrice and pass the setStockPrice to the component. Like shown below:
const streamStockPrice = (symbol, setStockPrice) => { setStockPrice(currentStockPrice) }
This way you set the stockprice in your main component and can use/show it there.
I am trying to use Google's model viewer to load 3D models. It has a button. But I want that button to be visible after the model is completely loaded. So, I used to use this Vanilla JavaScript code
<script>
const modelViewer = document.querySelector("model-viewer");
const button = document.getElementById("my_ar_button");
modelViewer.addEventListener("load", function() {
button.style.display = "block";
});
</script>
Now I am planning to use it in ReactJS and this is what it looks like
const modelViewer = document.querySelector("model-viewer");
const button = document.getElementById("my_ar_button");
modelViewer.addEventListener("load", function() {
button.style.display = "block";
});
<model-viewer
src="https://modelviewer.dev/shared-assets/models/reflective-sphere.gltf"
alt="A 3D model of an astronaut"
ar ar-modes="webxr scene-viewer quick-look"
ar-scale="auto"
quick-look-browsers="safari chrome"
ios-src="https://modelviewer.dev/shared-assets/models/Astronaut.usdz"
loading="eager"
poster="https://modelviewer.dev/assets/poster-astronaut.png"
autoplay
camera-controls
>
<button id="my_ar_button" class="my_ar_button" slot="ar-button">Show AR</button>
</model-viewer>
My CSS
.my_ar_button {
display: none;
}
I have added model-viewer using script tag
<Helmet>
<script
type="module"
src="https://unpkg.com/#google/model-viewer/dist/model-viewer.min.js"
async
>
</script>
</Helmet>
But it is giving me this error
TypeError: Cannot read property 'addEventListener' of null
You need to control <model-viewer> element within a React wrapper. See react-model-viewer, this is is their wrapper
const useModelLoader = (type, src): ModelData => {
const loader = useMemo(() => getLoader(type), [type]);
const [model, setModel] = useState(undefined);
const [modelCenter, setModelCenter] = useState<THREE.Vector3>(undefined);
const [error, setError] = useState(undefined);
const [progress, setProgress] = useState(0);
useEffect(() => {
loader.load(
// resource URL
src,
// called when the resource is loaded
model => setModel(model),
// called while loading is progressing
({ loaded, total }) => setProgress((loaded / total) * 100),
// called when loading has errors
error => setError(error)
);
}, [loader, src]);
// get the center of the model
useEffect(() => {
if (!model) {
return;
}
const box = new THREE.Box3();
box.setFromObject(model.scene);
const center = new THREE.Vector3();
box.getCenter(center);
setModelCenter(center);
}, [model]);
return { model, modelCenter, progress, error };
};
The 2nd useEffect() depends on model, so you can do the same with the button. This would be the React equivalent of modelViewer.addEventListener("load", function() {
const [showButton, setShowButton] = useState(false);
useEffect(() => {
if (model) { // when model has a value, the loader has completed
setShowButton(true); // now show the button
}
}, [model]);
const Button = showButton ? <button...> : null; // use this to wrap <button>
i need an opinion on how to solve this. i have the following code:
const {map, items} = props;
const [infoWindow, setInfoWindow] = useState(null);
const [renderedItems, setRenderedItems] = useState([]);
useEffect(() => {
const open = (marker, content) => {
infoWindow.close();
infoWindow.setContent(content)
infoWindow.open(map, marker);
}
if(map && items){
renderedItems.forEach(e => e.setMap(null));
const newRender = [];
items.forEach(e => {
const newMarker = new window.google.maps.Marker({
position: e.location
});
if(e.content){
newMarker.addListener("click", () => open(newMarker, e.content));
}
newRender.push(newMarker);
newMarker.setMap(map);
});
setRenderedItems(newRender);
}
}, [map, items, infoWindow]);
i keep having the react warning that renderedItems should be in the dependency. if i do that, this renders without end, but i cant take this out of here. cause i need to save the reference of this new created markers
it's normal that the warning pops up, it will check for every variable/function inside your useEffect, if u r certain that u don't need to trigger it when renderedItems change u can disable it:
useEffect(() => {
const open = (marker, content) => {
infoWindow.close();
infoWindow.setContent(content)
infoWindow.open(map, marker);
}
if(map && items){
renderedItems.forEach(e => e.setMap(null));
const newRender = [];
items.forEach(e => {
const newMarker = new window.google.maps.Marker({
position: e.location
});
if(e.content){
newMarker.addListener("click", () => open(newMarker, e.content));
}
newRender.push(newMarker);
newMarker.setMap(map);
});
setRenderedItems(newRender);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [map, items, infoWindow]);