The problem is that while using TS in my React project I need to set the type of the useRef()
function, but I don't know what it is. So as in the code below I'm setting it to any
and calling the leafletElement
afterwards. The problem that occurs is that the element is always undefined even after the map is referenced through the whenCreated
function isnide MapContainer
.
I am trying to set the MapContainer
in a way that it shows all the Marker
making it dynamically resizable so when the location is changed it updates it and adjusts the MapContainer
because it does not have a ref
property.
Example I'm trying to reproduce
const MapTry = () => {
const [nodes, setNodes] = useState<INode[]>([]);
const [devices, setDevices] = useState<IDevice[]>([]);
const [nodeDeviceTest, deviceNodeConnection] = useState([]);
var arrObj: any = [];
var arrOfBounds: any = [];
var mapRef = useRef<any>();
var groupRef = useRef<any>();
var device = new L.Icon({
iconUrl: deviceCar,
iconSize: [30, 30],
iconAnchor: [16, 3],
popupAnchor: [1, -1],
shadowSize: [41, 41]
});
var deviceNode = new L.Icon({
iconUrl: nodeDevice,
iconSize: [20, 30],
iconAnchor: [10, 0],
popupAnchor: [1, -34],
shadowSize: [41, 41],
})
useEffect(() => {
async function fetchData() {
const user = checkUserData();
const project = checkProjectData();
debugger;
if (mapRef.current && mapRef) {
let map = mapRef.current.leafletElement;
let group = groupRef.current.leafletElement;
map.fitBounds(group.getBounds());
}
const result = await getProjectNodeDeviceMap(project, user);
console.log(result);
if (result) {
if (result.devicesToNodes) {
for (var i = 0; i < result.devicesToNodes.length; i++) {
var objec = {
nodeId: "",
devId: "",
deviceLat: 0,
deviceLong: 0,
nodeLat: 0,
nodeLong: 0
}
for (var j = 0; j < result.devices.length; j++) {
if (result.devicesToNodes[i].deviceId == result.devices[j].deviceId) {
objec.deviceLat = result.devices[j].latitude
objec.deviceLong = result.devices[j].longitude
objec.devId = result.devices[j].deviceId
arrOfBounds.push([result.devices[j].latitude, result.devices[j].longitude])
for (var k = 0; k < result.nodes.length; k++) {
if (result.devicesToNodes[i].nodeId == result.nodes[k].id) {
objec.nodeLat = result.nodes[k].latitude
objec.nodeLong = result.nodes[k].longitude
objec.nodeId = result.nodes[k].id
arrOfBounds.push([result.nodes[k].latitude, result.nodes[k].longitude])
arrObj.push(objec);
break;
}
}
}
}
deviceNodeConnection(arrObj);
}
}
if (result.nodes) {
result.nodes.map((node: INode) => {
if (node.state == "running") {
node.color = "success";
}
else if (node.state == "error") {
node.color = "danger";
}
else if (node.state == "stopped") {
node.color = "warning";
}
else {
node.color = "info";
}
})
}
setNodes(result.nodes)
setDevices(result.devices)
}
}
const timer = setInterval(() => {
fetchData();
}, 3000)
return () => clearTimeout(timer);
}, [arrObj])
return (
<div className="col-md-9">
<div className="card shadow p-3 mb-5 bg-white rounded">
<div className="text-center">
<h3>Showcase</h3>
</div>
{devices && devices.length > 0 && <MapContainer
whenCreated={mapInstance => { mapRef.current = mapInstance }}
center={[devices[0].latitude, devices[0].longitude]}
zoom={6}
scrollWheelZoom={true}
minZoom={2}
maxBoundsViscosity={1.0}
zoomControl={false}
style={{ backgroundColor: "inherit", width: "100%", height: "60vh" }}
>
<TileLayer
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{nodeDeviceTest.length > 0 && nodeDeviceTest?.map((point: any) => {
return <Polyline key={point.deviceId} positions={[
[point.nodeLat, point.nodeLong], [point.deviceLat, point.deviceLong]
]} color={'blue'} dashArray="5,10" />
})}
<FeatureGroup ref={groupRef}>
{nodes?.map((point: any) => (
<Marker
zIndexOffset={100}
icon={deviceNode}
key={point.Id}
position={[
point.latitude,
point.longitude
]}
>
<Popup>
<br />
Node: {point.name}
<br />
State:
<span className={`text-${point.color}`}>
<b>{point.state}</b>
</span>
</Popup>
</Marker>
))}
{devices?.map((dev: any) => {
return <Marker
key={dev.deviceId}
position={[
dev.latitude,
dev.longitude
]}
icon={device}
>
<Popup>
Device id: {dev.deviceId}
</Popup>
</Marker>
})}
</FeatureGroup>
</MapContainer>}
</div>
</div>
)}
Also i hope the FeatureGroup
is properly referenced.Sorry if I missed something in my explanation. I am fairly new to React/TS.
EDIT: This is the example code I created
const Mapp = () => {
const devices = [{
deviceId: "1-528a-4f80-9c7a-124108f86895",
latitude: 45.5,
longitude: 18.7
},
{
deviceId: "2-528a-4f80-9c7a-124108f86895",
latitude: 60.5,
longitude: 18.7
}]
const nodes = [{
id: "test-node-1",
latitude: 45,
longitude: 15.4566,
name: "TestNode1",
state: "running",
},
{
id: "test-node-2",
latitude: 47,
longitude: 15.4566,
name: "TestNode2",
state: "running",
}]
const devicesToNodes = [{
deviceId: "1-528a-4f80-9c7a-124108f86895",
nodeId: "test-node-1"
},
{
deviceId: "2-528a-4f80-9c7a-124108f86895",
nodeId: "test-node-1"
}]
const arrOfLatLang = [{
deviceLat: 45.5,
deviceLong: 18.7,
nodeLat: 47,
nodeLong: 15.4566
},
{
deviceLat: 60.5,
deviceLong: 18.7,
nodeLat: 47,
nodeLong: 15.4566
}]
return (
<MapContainer
style={{ marginLeft: "200px" }}
center={[devices[0].latitude, devices[0].longitude]}
zoom={6}
scrollWheelZoom={true}
bounds={[[90, 180], [-90, 180]]}
minZoom={2}
maxBoundsViscosity={1.0}
>
<TileLayer
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{arrOfLatLang.length > 0 && arrOfLatLang?.map((point: any) => {
return <Polyline key={point.deviceId} positions={[
[point.nodeLat, point.nodeLong], [point.deviceLat, point.deviceLong]
]} color={'blue'} dashArray="5,10" />
})}
{nodes?.map((point: any) => (
<Marker
zIndexOffset={100}
key={point.Id}
position={[
point.latitude,
point.longitude
]}
>
<Popup>
<br />
Node: {point.name}
<br />
State:
<span className={`text-${point.color}`}>
<b>{point.state}</b>
</span>
</Popup>
</Marker>
))}
{devices?.map((dev: any) => {
return <Marker
key={dev.deviceId}
position={[
dev.latitude,
dev.longitude
]}
>
<Popup>
Device id: {dev.deviceId}
</Popup>
</Marker>
})}
</MapContainer>
)}
You use the underlying leaflet instance type. For the map, its L.Map
, for a featuregroup, its L.FeatureGroup
:
var mapRef = useRef<L.Map>();
var groupRef = useRef<L.FeatureGroup>();
Note that setting the map ref in whenCreated
, like you're doing with whenCreated={mapInstance => { mapRef.current = mapInstance }}
, is not a great idea, and may cause problems with the way you're trying to type the refs. I recommend using a proper ref prop on the MapContainer (ref={mapRef}
), or setting the mapRef
as a state variable instead, as described in the example in the react-leaflet docs.