refactor: Optimize map component rendering and memoize functions to improve performance

This commit is contained in:
2025-09-17 08:29:51 +02:00
parent 0bb0b07429
commit 1a49919000

View File

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