refactor: Optimize map component rendering and memoize functions to improve performance
This commit is contained in:
@@ -94,22 +94,8 @@ function ProjectsMapPageContent() {
|
|||||||
setMeasurementLine(null);
|
setMeasurementLine(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wrapper component for the map with proper loading state
|
// Status configuration with colors and labels - memoized
|
||||||
const MapWrapper = React.useMemo(() => {
|
const statusConfig = React.useMemo(() => ({
|
||||||
return function MapWrapperComponent(props) {
|
|
||||||
return (
|
|
||||||
<Suspense fallback={<MapLoadingComponent t={t} />}>
|
|
||||||
<DynamicMap
|
|
||||||
{...props}
|
|
||||||
isMeasuring={isMeasuring}
|
|
||||||
measurementPoints={measurementPoints}
|
|
||||||
onMeasurementClick={addMeasurementPoint}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}, [t, isMeasuring, measurementPoints, addMeasurementPoint]);
|
|
||||||
const statusConfig = {
|
|
||||||
registered: {
|
registered: {
|
||||||
color: "#6B7280",
|
color: "#6B7280",
|
||||||
label: formatProjectStatus("registered"),
|
label: formatProjectStatus("registered"),
|
||||||
@@ -135,7 +121,7 @@ function ProjectsMapPageContent() {
|
|||||||
label: formatProjectStatus("cancelled"),
|
label: formatProjectStatus("cancelled"),
|
||||||
shortLabel: "Wycofany",
|
shortLabel: "Wycofany",
|
||||||
},
|
},
|
||||||
};
|
}), []);
|
||||||
|
|
||||||
// Toggle all status filters
|
// Toggle all status filters
|
||||||
const toggleAllFilters = () => {
|
const toggleAllFilters = () => {
|
||||||
@@ -160,12 +146,12 @@ function ProjectsMapPageContent() {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Layer control functions
|
// Layer control functions - memoized to prevent re-renders
|
||||||
const handleBaseLayerChange = (layerName) => {
|
const handleBaseLayerChange = React.useCallback((layerName) => {
|
||||||
setActiveBaseLayer(layerName);
|
setActiveBaseLayer(layerName);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const toggleOverlay = (layerName) => {
|
const toggleOverlay = React.useCallback((layerName) => {
|
||||||
setActiveOverlays((prev) => {
|
setActiveOverlays((prev) => {
|
||||||
if (prev.includes(layerName)) {
|
if (prev.includes(layerName)) {
|
||||||
return prev.filter((name) => name !== layerName);
|
return prev.filter((name) => name !== layerName);
|
||||||
@@ -173,14 +159,14 @@ function ProjectsMapPageContent() {
|
|||||||
return [...prev, layerName];
|
return [...prev, layerName];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const toggleLayerPanel = () => {
|
const toggleLayerPanel = React.useCallback(() => {
|
||||||
setShowLayerPanel(!showLayerPanel);
|
setShowLayerPanel(!showLayerPanel);
|
||||||
};
|
}, [showLayerPanel]);
|
||||||
|
|
||||||
// Update URL with current map state (debounced to avoid too many updates)
|
// Update URL with current map state (debounced to avoid too many updates)
|
||||||
const updateURL = (center, zoom) => {
|
const updateURL = React.useCallback((center, zoom) => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.set("lat", center[0].toFixed(6));
|
params.set("lat", center[0].toFixed(6));
|
||||||
params.set("lng", center[1].toFixed(6));
|
params.set("lng", center[1].toFixed(6));
|
||||||
@@ -188,10 +174,10 @@ function ProjectsMapPageContent() {
|
|||||||
|
|
||||||
// Use replace to avoid cluttering browser history
|
// Use replace to avoid cluttering browser history
|
||||||
router.replace(`/projects/map?${params.toString()}`, { scroll: false });
|
router.replace(`/projects/map?${params.toString()}`, { scroll: false });
|
||||||
};
|
}, [router]);
|
||||||
|
|
||||||
// Handle map view changes with debouncing
|
// Handle map view changes with debouncing - memoized
|
||||||
const handleMapViewChange = (center, zoom) => {
|
const handleMapViewChange = React.useCallback((center, zoom) => {
|
||||||
setMapCenter(center);
|
setMapCenter(center);
|
||||||
setMapZoom(zoom);
|
setMapZoom(zoom);
|
||||||
|
|
||||||
@@ -200,7 +186,7 @@ function ProjectsMapPageContent() {
|
|||||||
window.mapUpdateTimeout = setTimeout(() => {
|
window.mapUpdateTimeout = setTimeout(() => {
|
||||||
updateURL(center, zoom);
|
updateURL(center, zoom);
|
||||||
}, 500); // Wait 500ms after the last move to update URL
|
}, 500); // Wait 500ms after the last move to update URL
|
||||||
};
|
}, [updateURL]);
|
||||||
|
|
||||||
// Hide navigation and ensure full-screen layout
|
// Hide navigation and ensure full-screen layout
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -298,38 +284,39 @@ function ProjectsMapPageContent() {
|
|||||||
}
|
}
|
||||||
}, [currentTool, isMeasuring]);
|
}, [currentTool, isMeasuring]);
|
||||||
|
|
||||||
// Convert projects to map markers with filtering
|
// Convert projects to map markers with filtering - memoized to prevent re-renders
|
||||||
const markers = projects
|
const markers = React.useMemo(() => {
|
||||||
.filter((project) => project.coordinates)
|
return projects
|
||||||
.filter((project) => statusFilters[project.project_status] !== false)
|
.filter((project) => project.coordinates)
|
||||||
.map((project) => {
|
.filter((project) => statusFilters[project.project_status] !== false)
|
||||||
const [lat, lng] = project.coordinates
|
.map((project) => {
|
||||||
.split(",")
|
const [lat, lng] = project.coordinates
|
||||||
.map((coord) => parseFloat(coord.trim()));
|
.split(",")
|
||||||
if (isNaN(lat) || isNaN(lng)) {
|
.map((coord) => parseFloat(coord.trim()));
|
||||||
return null;
|
if (isNaN(lat) || isNaN(lng)) {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const statusInfo =
|
const statusInfo =
|
||||||
statusConfig[project.project_status] || statusConfig.registered;
|
statusConfig[project.project_status] || statusConfig.registered;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: [lat, lng],
|
position: [lat, lng],
|
||||||
color: statusInfo.color,
|
color: statusInfo.color,
|
||||||
popup: (
|
popup: (
|
||||||
<div className="min-w-72 max-w-80">
|
<div className="min-w-72 max-w-80">
|
||||||
<div className="mb-3 pb-2 border-b border-gray-200">
|
<div className="mb-3 pb-2 border-b border-gray-200">
|
||||||
<h3 className="font-semibold text-base mb-1 text-gray-900">
|
<h3 className="font-semibold text-base mb-1 text-gray-900">
|
||||||
{project.project_name}
|
{project.project_name}
|
||||||
</h3>
|
</h3>
|
||||||
{project.project_number && (
|
{project.project_number && (
|
||||||
<div className="inline-block bg-blue-100 text-blue-800 text-xs font-medium px-2 py-1 rounded-full">
|
<div className="inline-block bg-blue-100 text-blue-800 text-xs font-medium px-2 py-1 rounded-full">
|
||||||
{project.project_number}
|
{project.project_number}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2 text-sm text-gray-600 mb-3">
|
<div className="space-y-2 text-sm text-gray-600 mb-3">
|
||||||
{project.address && (
|
{project.address && (
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<svg
|
<svg
|
||||||
@@ -419,6 +406,7 @@ function ProjectsMapPageContent() {
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((marker) => marker !== null);
|
.filter((marker) => marker !== null);
|
||||||
|
}, [projects, statusFilters, statusConfig, t]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
@@ -980,7 +968,7 @@ function ProjectsMapPageContent() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<MapWrapper
|
<DynamicMap
|
||||||
center={mapCenter}
|
center={mapCenter}
|
||||||
zoom={mapZoom}
|
zoom={mapZoom}
|
||||||
markers={markers}
|
markers={markers}
|
||||||
|
|||||||
Reference in New Issue
Block a user