Reputation: 432
I am trying to display some long and latitude data on a map when the page is loaded but it seems my page is rendered faster than the data can be fetched.
The data is retrieved properly I have tested the api call.
Can anyone see what I am missing
I tried using useEffect hook but that still does not seem to work:
here is my component:
import react from "react";
import { useState, useEffect } from "react";
import { MapContainer, TileLayer, Popup, Polyline } from "react-leaflet";
import axios from "axios";
import polyline from "@mapbox/polyline";
function Map() {
const [activites, setActivities] = useState([]);
const [polylines, setPolylines] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
setActivitieData();
setPolylineArray();
setIsLoading(false);
}, []);
const getActivityData = async () => {
const response = await axios.get("http://localhost:8080/api/data");
return response.data;
};
const setActivitieData = async () => {
const activityData = await getActivityData();
setActivities(activityData);
};
const setPolylineArray = () => {
const polylineArray = [];
for (let i = 0; i < activites.length; i++) {
const polylineData = activites[i].map.summary_polyline;
const activityName = activites[i].name;
polylineArray.push({ positions: polyline.decode(polylineData), name: activityName });
}
setPolylines(polylineArray);
console.log("Polyline array = ", polylineArray);
};
return !isLoading ? (
<MapContainer center={[37.229564, -120.047533]} zoom={15} scrollWheelZoom={false} style={{ width: "100%", height: "calc(100vh - 4rem)" }}>
<TileLayer
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{polylines.map((activity, idx) => (
<Polyline key={idx} positions={activity.positions}>
<Popup>
<div>
<h2>{"Name: " + activity.name}</h2>
</div>
</Popup>
</Polyline>
))}
</MapContainer>
) : (
<div>
<p>Loading</p>
</div>
);
}
export default Map;
Upvotes: 1
Views: 90
Reputation: 169075
You'll have a better time with
swr
to deal with the state related to getting data from your API (beyond just data
, you might be interested in e.g. isError
and isValidating
; please refer to the docs),useMemo
for the derived polyline state (because it is always purely derivable from the activities
you've fetched, and doesn't need to be separately updated state).Something like this (I'm understandably unable to test this without your API and all, but it should work).
import { MapContainer, Polyline, Popup, TileLayer } from "react-leaflet";
import axios from "axios";
import polyline from "@mapbox/polyline";
import React from "react";
import useSWR from "swr";
function getJSON(url) {
return axios.get(url).then((res) => res.data);
}
function Map() {
const activitySWR = useSWR("http://localhost:8080/api/data", getJSON);
const activities = activitySWR.data ?? null;
const polylines = React.useMemo(() => {
if (!activities) return null; // no data yet
const polylineArray = [];
for (const item of activities) {
const polylineData = item.map.summary_polyline;
polylineArray.push({ positions: polyline.decode(polylineData), name: item.name });
}
return polylineArray;
}, [activities]);
if (!activities) {
return <div>Loading...</div>;
}
return (
<MapContainer
center={[37.229564, -120.047533]}
zoom={15}
scrollWheelZoom={false}
style={{ width: "100%", height: "calc(100vh - 4rem)" }}
>
<TileLayer
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{polylines.map((activity, idx) => (
<Polyline key={idx} positions={activity.positions}>
<Popup>
<div>
<h2>{`Name: ${activity.name}`}</h2>
</div>
</Popup>
</Polyline>
))}
</MapContainer>
);
}
export default Map;
Upvotes: 4
Reputation: 431
You have two options here.
useEffect(() => {
if(activites.length){
setPolylineArray();
}
setIsLoading(false);
}, [activites])
useEffect(() => {
(async () => {
const activities= await getActivityData();
setPolylineArray(activities);
setIsLoading(false);
})();
}, [])
Upvotes: 1
Reputation: 14833
You could try something like this potentially.
This way you can hopefully guarantee the data is available before trying to access it.
useEffect(() => {
setActivitieData().then(() => {
setPolylineArray();
setIsLoading(false);
});
}, []);
Another route would be to pass the data down as well like the following:
Note: I fixed some spelling so this may not copy/paste for you.
useEffect(() => {
setActivitiesData().then((activities) => {
setPolylineArray(activities);
setIsLoading(false);
});
}, []);
const getActivityData = async () => {
const response = await axios.get("http://localhost:8080/api/data");
return response.data;
};
const setActivitiesData = async () => {
const activityData = await getActivityData();
setActivities(activityData);
return activityData
};
const setPolylineArray = (activities) => {
// Do what you want with activities here
};
Upvotes: 0