När en sökfunktion implementeras i React, utlöses `onChange`-händelsehanteraren och sökfunktionen anropas varje gång användaren skriver i sökrutan. Detta kan leda till prestandaproblem, särskilt vid API-anrop eller databasfrågor. Frekventa anrop till sökfunktionen kan överbelasta webbservern, vilket kan orsaka krascher eller göra användargränssnittet osvarigt. Debouncing är en teknik som hjälper till att lösa det här problemet.
Vad är debouncing?
En traditionell implementering av en sökfunktion i React innebär att en `onChange`-hanterarfunktion anropas vid varje tangenttryckning, vilket visas i exemplet nedan:
import { useState } from "react"; export default function Search() { const [searchTerm, setSearchTerm] = useState(""); const handleSearch = () => { console.log("Sök efter:", searchTerm); }; const handleChange = (e) => { setSearchTerm(e.target.value); handleSearch(); }; return ( <input onChange={handleChange} value={searchTerm} placeholder="Sök här..." /> ); }
Denna metod fungerar, men kan bli kostsam om varje tangenttryckning orsakar ett anrop till backend för att uppdatera sökresultaten. Om du t.ex. söker efter ”webdev” skulle applikationen skicka förfrågningar med värdena ”w”, ”we”, ”web” osv. till backend.
Debouncing fungerar genom att fördröja exekveringen av en funktion tills en viss fördröjningsperiod har passerat utan att användaren har interagerat. Debounce-funktionen upptäcker när användaren skriver, och hindrar anropet till sökhanteraren tills fördröjningen har gått ut. Om användaren fortsätter att skriva inom fördröjningsperioden, återställs timern och React anropar funktionen igen efter den nya fördröjningen. Denna process fortgår tills användaren slutar skriva.
Genom att vänta på att användaren ska pausa med att skriva, säkerställer debouncing att applikationen endast gör nödvändiga sökförfrågningar, vilket minskar belastningen på servern.
Hur man implementerar debouncing för sökning i React
Det finns olika bibliotek som kan användas för att implementera debouncing. Alternativt kan du implementera det manuellt med hjälp av JavaScripts `setTimeout`- och `clearTimeout`-funktioner.
I det här exemplet används debounce-funktionen från lodash-biblioteket.
Förutsatt att du har ett React-projekt, skapa en ny komponent som heter `Search`. Om du inte har något projekt kan du skapa en med hjälp av `create-react-app`.
Kopiera följande kod till `Search`-komponentfilen för att skapa en sökruta som anropar en hanterarfunktion vid varje tangenttryckning.
import { useState } from "react"; export default function Search() { const [searchTerm, setSearchTerm] = useState(""); const handleSearch = () => { console.log("Sök efter:", searchTerm); }; const handleChange = (e) => { setSearchTerm(e.target.value); handleSearch(); }; return ( <input onChange={handleChange} value={searchTerm} placeholder="Sök här..." /> ); }
För att implementera debouncing för `handleSearch`-funktionen, skickas den till debounce-funktionen från lodash.
import debounce from "lodash.debounce"; import { useState } from "react"; export default function Search() { const [searchTerm, setSearchTerm] = useState(""); const handleSearch = () => { console.log("Sök efter:", searchTerm); }; const debouncedSearch = debounce(handleSearch, 1000); const handleChange = (e) => { setSearchTerm(e.target.value); debouncedSearch(); }; return ( <input onChange={handleChange} value={searchTerm} placeholder="Sök här..." /> ); }
I debounce-funktionen anges den funktion som ska fördröjas (dvs. `handleSearch`) och fördröjningstiden i millisekunder, t.ex. 500ms.
Trots att koden ovan verkar fördröja anropet till `handleSearch` tills användaren pausar med att skriva, så kommer det inte att fungera optimalt i React. Anledningen förklaras i nästa avsnitt.
Debouncing och omrenderingar
Applikationen använder sig av en kontrollerad inmatning. Det innebär att värdet på inmatningsfältet styrs av ett tillståndsvärde. Varje gång användaren skriver i sökfältet, uppdaterar React detta tillstånd.
I React, när ett tillståndsvärde ändras, kommer React att återrapportera komponenten och alla dess funktioner.
I `Search`-komponenten, när komponenten omrenderas, exekverar React debounce-funktionen. Funktionen skapar en ny timer som håller reda på fördröjningen och den gamla timern hamnar i minnet. När tiden har gått ut, aktiveras sökfunktionen. Det innebär att sökfunktionen aldrig debounced, utan endast är fördröjd med 500ms. Denna cykel upprepas vid varje rendering – en ny timer skapas, den gamla timern går ut och sedan anropas sökfunktionen.
För att debounce-funktionen ska fungera korrekt behöver den endast anropas en gång. Detta kan åstadkommas genom att definiera debounce-funktionen utanför komponenten eller genom att använda memoiseringstekniken. Genom detta så kommer React inte att exekvera den igen vid omrendering.
Definiera debounce-funktionen utanför sökkomponenten
Flytta debounce-funktionen utanför sökkomponenten enligt nedan:
import debounce from "lodash.debounce" const handleSearch = (searchTerm) => { console.log("Sök efter:", searchTerm); }; const debouncedSearch = debounce(handleSearch, 500);
Anropa `debouncedSearch` i sökkomponenten och skicka med söktermen.
export default function Search() { const [searchTerm, setSearchTerm] = useState(""); const handleChange = (e) => { setSearchTerm(e.target.value); debouncedSearch(searchTerm); }; return ( <input onChange={handleChange} value={searchTerm} placeholder="Sök här..." /> ); }
Sökfunktionen anropas nu endast efter fördröjningsperioden.
Memoisera debounce-funktionen
Memoisering innebär att man sparar resultaten av en funktion och återanvänder dem när funktionen anropas med samma argument.
För att memoisera debounce-funktionen, använd `useMemo`-hooken.
import debounce from "lodash.debounce"; import { useCallback, useMemo, useState } from "react"; export default function Search() { const [searchTerm, setSearchTerm] = useState(""); const handleSearch = useCallback((searchTerm) => { console.log("Sök efter:", searchTerm); }, []); const debouncedSearch = useMemo(() => { return debounce(handleSearch, 500); }, [handleSearch]); const handleChange = (e) => { setSearchTerm(e.target.value); debouncedSearch(searchTerm); }; return ( <input onChange={handleChange} value={searchTerm} placeholder="Sök här..." /> ); }
Observera att `handleSearch`-funktionen även har lagts in i en `useCallback`-hook för att säkerställa att React endast anropar den en gång. Utan `useCallback`-hooken skulle React köra `handleSearch`-funktionen vid varje omrendering, vilket skulle göra att beroendena för `useMemo`-hooken ändras, och i sin tur anropa debounce-funktionen.
Nu kommer React endast att anropa debounce-funktionen om `handleSearch`-funktionen eller fördröjningstiden ändras.
Optimera sökning med debouncing
Ibland kan det vara mer fördelaktigt för prestandan att implementera en fördröjning. Vid hantering av sökuppgifter, speciellt med komplexa databas- eller API-anrop, är användningen av en debounce-funktion ett effektivt tillvägagångssätt. Denna funktion introducerar en tidsfördröjning innan förfrågningar skickas till backend.
Detta hjälper till att minska antalet förfrågningar till servern, då förfrågningen endast skickas efter att fördröjningen löpt ut och användaren har slutat skriva. På så sätt överbelastas inte servern med onödiga förfrågningar och prestandan bibehålls effektiv.