Существует два подхода к реализации дебаунса для живого поиска (search by text) в React:
<b>1.</b> <br/>
<i>Создание функции для дебаунса</i> <br/>
<pre><code class="javascript">export const debounce=(cb:Function,delay:number)=> {
let timer:any
return (...args:any[])=> {
clearTimeout(timer);
timer = setTimeout(()=>cb(...args),delay);
}
}</code></pre> <br/>
<i>Применение в компоненте</i> <br/>
<pre><code class="javascript">const [searchText, setSearchText] = React.useState("");
const [error, setError] = React.useState("");
const [filterResults, setFilterResults] = React.useState([]);
const handleFilter = async (searchText: string) => {
const res = await filterByName(searchText);
if (res.error) {
setError(res.error);
} else {
setFilterResults(res.results);
}
}
const debouncedSearchText = useCallback(debounce((value: string) => handleFilter(value), 300), [])
useEffect(() => {
setError("")
if (searchText.length > 2 || !searchText) {
debouncedSearchText(searchText);
}
}, [searchText]);</code></pre> <br/>
<br/>
<b>2.</b> <br/>
<i>Создание хука для дебаунса и его использование</i> <br/>
Хук: <br/>
<pre><code class="javascript">export const useDebounce = (dbValue: string, delay: number) => {
const [value, setValue] = React.useState(dbValue);
useEffect(() => {
let timer = setTimeout(() => {
setValue(dbValue)
}, delay);
return () => {
clearTimeout(timer)
};
}, [dbValue])
return value
}</code></pre> <br/>
<br/>
<pre><code class="javascript">const [searchText, setSearchText]...
Оба подхода, которые вы описали, являются корректными методами реализации функции дебаунса в React, и каждый из них имеет свои преимущества. Давайте рассмотрим каждый из них более подробно и приведем полный пример использования.
### 1. Создание функции для дебаунса
В этом методе мы создаем отдельную функцию для дебаунса и используем ее в компоненте. Отправляем обновленный текст через `debouncedSearchText`, когда происходит изменение состояния `searchText`.
```javascript
// debounce.js
export const debounce = (cb: Function, delay: number) => {
let timer: NodeJS.Timeout;
return (...args: any[]) => {
clearTimeout(timer);
timer = setTimeout(() => cb(...args), delay);
}
};
// Component.js
import React, { useState, useEffect, useCallback } from 'react';
import { debounce } from './debounce';
import { filterByName } from './api'; // Предположим, что это ваша функция асинхронного запроса
const SearchComponent = () => {
const [searchText, setSearchText] = useState("");
const [error, setError] = useState("");
const [filterResults, setFilterResults] = useState([]);
const handleFilter = async (searchText: string) => {
const res = await filterByName(searchText);
if (res.error) {
setError(res.error);
} else {
setFilterResults(res.results);
}
};
const debouncedSearchText = useCallback(debounce((value: string) => handleFilter(value), 300), []);
useEffect(() => {
setError("");
if (searchText.length > 2 || !searchText) {
debouncedSearchText(searchText);
}
}, [searchText]);
return (
<div>
<input
type="text"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
placeholder="Search..."
/>
{error && <div>{error}</div>}
<ul>
{filterResults.map(result => (
<li key={result.id}>{result.name}</li> // пример отображения результатов
))}
</ul>
</div>
);
};
```
### 2. Создание хука для дебаунса
Этот подход может быть более элегантным и позволяет более удобно использовать дебаунс в разных компонентах. Создание пользовательского хука делает его повторно используемым.
```javascript
// useDebounce.js
import { useEffect, useState } from 'react';
export const useDebounce = (value: string, delay: number) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
};
// Component.js
import React, { useState, useEffect } from 'react';
import { useDebounce } from './useDebounce';
import { filterByName } from './api';
const SearchComponent = () => {
const [searchText, setSearchText] = useState("");
const [error, setError] = useState("");
const [filterResults, setFilterResults] = useState([]);
// Используем наш хук для дебаунса
const debouncedSearchText = useDebounce(searchText, 300);
useEffect(() => {
const search = async () => {
if (debouncedSearchText.length > 2 || !debouncedSearchText) {
setError("");
const res = await filterByName(debouncedSearchText);
if (res.error) {
setError(res.error);
} else {
setFilterResults(res.results);
}
}
};
search();
}, [debouncedSearchText]);
return (
<div>
<input
type="text"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
placeholder="Search..."
/>
{error && <div>{error}</div>}
<ul>
{filterResults.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
);
};
```
### Заключение
Оба подхода имеют свои применения — если вам нужно использовать дебаунс в нескольких местах в вашем приложении, создание пользовательского хука будет хорошим выбором. В то время как прямое использование функции дебаунса
Оба варианта рабочие, но. <br/> - Хук не используешь в циклах и условиях. <br/> - Хук не используешь вне реакта. <br/> <br/> - Хук более многословен, для 4 инпутов понадобится 4 вызова хука, 4 состояния и 4 эффекта для работы с каждым по отдельности - это весомый минус, а для useCallback с дебаунсом всего 4: <br/> <pre><code class="jsx">const debouncedSearchText = useCallback(debounce((value) => console.log(value), 300), []);
const debouncedSearchText = useCallback(debounce((value) => console.log(value), 300), []);
const debouncedSearchText = useCallback(debounce((value) => console.log(value), 300), []);
const debouncedSearchText = useCallback(debounce((value) => console.log(value), 300), []);</code></pre> <br/> <b>VS</b> <br/> <pre><code class="jsx">const [inputValue, setInputValue] = useState('');
const debouncedValue = useDebounce(inputValue, 300);
useEffect(() => {
console.log(debouncedValue);
}, [debouncedValue]);
const [inputValue, setInputValue] = useState('');
const debouncedValue = useDebounce(inputValue, 300);
useEffect(() => {
console.log(debouncedValue);
}, [debouncedValue]);
const [inputValue, setInputValue] = useState('');
const debouncedValue = useDebounce(inputValue, 300);
useEffect(() => {
console.log(debouncedValue);
}, [debouncedValue]);
const [inputValue, setInputValue] = useState('');
const debouncedValue = useDebounce(inputValue, 300);
useEffect(() => {
console.log(debouncedValue);
}, [debouncedValue]);</code></pre> <br/> <br/> Поэтому я считаю, что первый вариант намного предпочтительнее.