[frontend]: Fix search input freezing when lots of data

This commit is contained in:
2025-03-12 13:37:33 +00:00
parent 34b9b92933
commit 9246540db1

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from "react"; import React, { useState, useEffect, useMemo, useRef } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Navbar from "./components/Navbar"; import Navbar from "./components/Navbar";
@ -28,20 +28,26 @@ function App() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [clusteringEnabled, setClusteringEnabled] = useState(true); const [clusteringEnabled, setClusteringEnabled] = useState(true);
// Search states: one is the raw user input, the other is the actual term we filter on
const [searchInput, setSearchInput] = useState("");
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const debounceTimeout = useRef(null);
// Debounce effect const [filteredMarkers, setFilteredMarkers] = useState([]);
useEffect(() => { const [numMarkers, setNumMarkers] = useState(0);
const handler = setTimeout(() => {
setSearchTerm(searchInput); const handleSearchChange = (e) => {
const value = e.target.value;
// Clear any existing timeout to reset the debounce timer
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
// Set a new timeout to update the state only after 300ms of inactivity
debounceTimeout.current = setTimeout(() => {
setSearchTerm(value); // Only update state after delay
}, 300); }, 300);
};
return () => {
clearTimeout(handler);
};
}, [searchInput]);
const fetchData = async (enabledSources) => { const fetchData = async (enabledSources) => {
setLoading(true); setLoading(true);
@ -282,6 +288,7 @@ function App() {
}) })
.filter((marker) => marker.display); .filter((marker) => marker.display);
setNumMarkers(newMarkers.length);
setMarkers(newMarkers); setMarkers(newMarkers);
} catch (error) { } catch (error) {
console.error("Error fetching data:", error); console.error("Error fetching data:", error);
@ -290,15 +297,52 @@ function App() {
}; };
// 2. Memoize the filtered markers so it recalculates only if `searchTerm` or `markers` changes // 2. Memoize the filtered markers so it recalculates only if `searchTerm` or `markers` changes
const filteredMarkers = useMemo(() => { // const filteredMarkers = useMemo(() => {
if (!searchTerm.trim()) { // setLoading(true);
return markers; // console.log("set loading true");
} //
return markers.filter((marker) => // if (!searchTerm.trim()) {
// setLoading(false);
// return markers;
// }
// const newMarkers = markers.filter((marker) =>
// marker.markerText.includes(searchTerm.toLowerCase())
// );
//
// setLoading(false);
// console.log("set loading false");
// return newMarkers;
//
// }, [searchTerm, markers]);
const memoizedFilteredMarkers = useMemo(() => {
return markers.filter(marker =>
marker.markerText.includes(searchTerm.toLowerCase()) marker.markerText.includes(searchTerm.toLowerCase())
); );
}, [searchTerm, markers]); }, [searchTerm, markers]);
useEffect(() => {
if (numMarkers > 500) {
setLoading(true); // Start loading immediately
}
const timeout = setTimeout(() => {
setFilteredMarkers(memoizedFilteredMarkers); // Update markers
// Now wait 10 seconds before setting loading to false
if (numMarkers > 500) {
const loadingTimeout = setTimeout(() => {
setLoading(false); // Stop loading after 10 seconds
}, 5000);
}
return () => clearTimeout(loadingTimeout); // Cleanup loading timeout
}, 0); // Small debounce before filtering
return () => clearTimeout(timeout); // Cleanup initial debounce timeout
}, [memoizedFilteredMarkers]);
return ( return (
<Router> <Router>
<Navbar /> <Navbar />
@ -320,8 +364,8 @@ function App() {
> >
<input <input
type="text" type="text"
value={searchInput} // value={searchInput}
onChange={(e) => setSearchInput(e.target.value)} onChange={(e) => handleSearchChange(e)}
placeholder="Search..." placeholder="Search..."
style={{ style={{
width: "250px", fontSize: "16px", width: "250px", fontSize: "16px",