Jak zlepšit výkon vyhledávání v reakci na debouncing

Při vytváření funkcionality vyhledávání v Reactu se často stává, že se obsluha `onChange` volá s každým zadaným znakem. Tento přístup ale může mít negativní dopad na výkon aplikace, zejména když se při každém stisku klávesy volá API nebo se provádí dotaz do databáze. Příliš časté volání vyhledávací funkce může přetížit server a vést k problémům s odezvou uživatelského rozhraní. Technika zvaná debouncing efektivně řeší tento problém.

Co je to debouncing?

Obvykle se funkce vyhledávání v Reactu implementuje tak, že se při každém stisku klávesy volá obsluha `onChange`. Ilustruje to následující příklad:

 import { useState } from "react";

export default function Search() {
const [searchTerm, setSearchTerm] = useState("");

const handleSearch = () => {
console.log("Hledat:", searchTerm);
};

const handleChange = (e) => {
setSearchTerm(e.target.value);
handleSearch();
};

return (
<input
onChange={handleChange}
value={searchTerm}
placeholder="Zadejte hledaný výraz..."
/>
);
}

I když tento přístup funguje, odesílání požadavků na server při každém stisku klávesy může být náročné. Pokud byste například vyhledávali „programování“, aplikace by poslala na server požadavky s hodnotami „p“, „pr“, „pro“ a tak dále.

Debouncing je technika, která zpožďuje provedení funkce, dokud neuplyne definovaná prodleva. Debouncing detekuje, kdy uživatel zadává text, a zablokuje volání obsluhy vyhledávání, dokud neuplyne stanovená doba. Pokud uživatel pokračuje v psaní během této prodlevy, časovač se resetuje a React znovu vyvolá funkci pro nové zpoždění. Tento cyklus pokračuje, dokud uživatel nepřestane psát.

Díky tomu, že se čeká, dokud uživatelé nedokončí psaní, debouncing zajistí, že aplikace odešle pouze nezbytné vyhledávací požadavky, což sníží zatížení serveru.

Jak implementovat Debounce pro vyhledávání v Reactu?

Existuje několik knihoven, které vám pomohou s implementací debounce. Můžete si ji také implementovat sami za pomocí funkcí `setTimeout` a `clearTimeout` v JavaScriptu.

Tento článek využívá funkci debounce z knihovny lodash.

Za předpokladu, že máte připravený projekt React, vytvořte novou komponentu s názvem Search. Pokud ještě projekt nemáte, můžete ho vytvořit pomocí nástroje Create React App.

Do souboru komponenty Search vložte následující kód, který vytvoří vstupní pole a při každém stisku klávesy volá obslužnou funkci.

 import { useState } from "react";

export default function Search() {
const [searchTerm, setSearchTerm] = useState("");

const handleSearch = () => {
console.log("Hledat:", searchTerm);
};

const handleChange = (e) => {
setSearchTerm(e.target.value);
handleSearch();
};

return (
<input
onChange={handleChange}
value={searchTerm}
placeholder="Zadejte hledaný výraz..."
/>
);
}

Pro odložení volání funkce `handleSearch` použijte funkci `debounce` z knihovny lodash.

 import debounce from "lodash.debounce";
import { useState } from "react";

export default function Search() {
const [searchTerm, setSearchTerm] = useState("");

const handleSearch = () => {
console.log("Hledat:", searchTerm);
};
const debouncedSearch = debounce(handleSearch, 1000);

const handleChange = (e) => {
setSearchTerm(e.target.value);
debouncedSearch();
};

return (
<input
onChange={handleChange}
value={searchTerm}
placeholder="Zadejte hledaný výraz..."
/>
);
}

Ve funkci `debounce` předáváte funkci, kterou chcete odložit (v tomto případě `handleSearch`) a dobu prodlevy v milisekundách (1000 ms, tj. 1 sekunda).

Ačkoliv by výše uvedený kód měl odložit volání funkce `handleSearch` až do chvíle, kdy uživatel přestane psát, v Reactu takto nefunguje. Následující sekce vysvětlí, proč tomu tak je.

Debouncing a opětovné vykreslování

Tato aplikace používá řízený vstup. To znamená, že stav řídí hodnotu vstupu. Pokaždé, když uživatel zadá znak, React aktualizuje stav.

Když se hodnota stavu v Reactu změní, dojde k opětovnému vykreslení komponenty a tím se vykonají i všechny funkce v ní definované.

V komponentě vyhledávání, když se komponenta znovu vykreslí, React spustí funkci `debounce`. Funkce vytvoří nový časovač, který sleduje odloženou dobu, a starý časovač zůstane uložený v paměti. Jakmile čas vyprší, zavolá se funkce vyhledávání. To znamená, že funkce vyhledávání není nikdy „debounced“, jen se odloží o 500 ms. Tento cyklus se opakuje při každém vykreslení – funkce vytvoří nový časovač, starý časovač vyprší a následně se zavolá vyhledávací funkce.

Aby `debounce` fungovalo správně, musíte ji zavolat jen jednou. Můžete toho dosáhnout voláním funkce `debounce` mimo komponentu, nebo použitím techniky memoizace. Díky tomu React, i když dojde k opětovnému vykreslení komponenty, tuto funkci znovu nespustí.

Definování funkce `debounce` mimo komponentu vyhledávání

Přesuňte definici funkce `debounce` mimo komponentu Search, jak ukazuje následující příklad:

 import debounce from "lodash.debounce"

const handleSearch = (searchTerm) => {
console.log("Hledat:", searchTerm);
};

const debouncedSearch = debounce(handleSearch, 500);

Nyní v komponentě Search volejte `debouncedSearch` a předejte hledaný výraz.

 export default function Search() {
const [searchTerm, setSearchTerm] = useState("");

const handleChange = (e) => {
setSearchTerm(e.target.value);
debouncedSearch(searchTerm);
};

return (
<input
onChange={handleChange}
value={searchTerm}
placeholder="Zadejte hledaný výraz..."
/>
);
}

Funkce vyhledávání se zavolá až po uplynutí nastavené prodlevy.

Memoizace funkce `Debounce`

Memoizace je technika, kdy se ukládají výsledky volání funkce do mezipaměti. Při opětovném volání funkce se stejnými argumenty se použije výsledek z mezipaměti, namísto toho, aby se funkce znovu spouštěla.

Pro memoizaci funkce `debounce` použijte hook `useMemo`.

 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("Hledat:", 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="Zadejte hledaný výraz..."
/>
);
}

Všimněte si, že funkci `handleSearch` jsme také obalili hookem `useCallback`, aby ji React volal jen jednou. Bez hooku `useCallback` by React spouštěl funkci `handleSearch` při každém opětovném vykreslení, což by změnilo závislosti hooku `useMemo`, a tím by se znovu volala funkce `debounce`.

Nyní React zavolá funkci `debounce` pouze tehdy, když se změní funkce `handleSearch`, nebo doba prodlevy.

Optimalizace vyhledávání pomocí Debounce

Zpomalení odesílání požadavků může někdy vést k lepšímu výkonu. Při zpracování úloh vyhledávání, zejména náročných databázových operací nebo volání API, je použití debounce tou správnou cestou. Tato funkce vkládá prodlevu před odesláním požadavků na backend.

Pomáhá snížit počet požadavků na server, protože se požadavek odesílá až po uplynutí prodlevy, poté, co uživatel přestane psát. Díky tomu se server nepřetěžuje příliš mnoha požadavky a výkon zůstává efektivní.