6

Как реализовать дебаунс?

1

Как осуществить дебаунс в React?

Я хочу применить дебаунс к функции handleOnChange.

Я попытался использовать debounce(this.handleOnChange, 200), но это не работает.

Вот мой код:

function debounce(fn, delay) {
  var timer = null;
  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  };
}

var SearchBox = React.createClass({
  render: function() {
    return <input type="search" name="p" onChange={this.handleOnChange} />;
  },

  handleOnChange: function(event) {
    // Выполняем Ajax запрос
  }
});

Может кто-то подскажет, как правильно применить дебаунс в данном случае?

5 ответ(ов)

0

2022 - используйте хук useEffect

На данный момент вашим лучшим вариантом будет использование хука useEffect. Этот хук позволяет задавать функцию, которая может изменять состояние в ответ на асинхронные события. Дебаунс является асинхронным процессом, поэтому useEffect идеально подходит для этой задачи.

Если из хука вернуть функцию, она будет вызвана перед тем, как хук будет вызван снова. Это позволяет отменить предыдущий тайм-аут, эффективно реализуя дебаунс.

Пример

В этом примере у нас есть два состояния: value и tempValue. Установка tempValue запустит хук useEffect, который начнет тайм-аут на 1000 мс, по истечении которого будет вызвана функция для копирования значения tempValue в value.

Хук возвращает функцию, которая очищает таймер. Когда хук вызывается снова (например, нажата другая клавиша), тайм-аут отменяется и сбрасывается.

const DebounceDemo = () => {
  const [value, setValue] = useState();
  const [tempValue, setTempValue] = useState();

  // Этот хук устанавливает таймер на 1000 мс для копирования tempValue в value
  // Если хук вызывается снова, таймер будет отменен
  // Это создает дебаунс
  useEffect(
    () => {
      // Ждем 1000 мс перед копированием значения tempValue в value
      const timeout = setTimeout(() => {
        setValue(tempValue);
      }, 1000);

      // Если хук вызывается снова, отменяем предыдущий тайм-аут
      // Это создает дебаунс вместо задержки
      return () => clearTimeout(timeout);
    },
    // Запускаем хук каждый раз, когда пользователь нажимает клавишу
    [tempValue]
  );

  // Здесь мы создаем input для установки tempValue.
  // Значение value будет обновлено через 1000 мс после последнего вызова хуки,
  // т.е. после последнего нажатия клавиши пользователем.
  return (
    <>
      <input
        onChange={
          ({ target }) => setTempValue(target.value)
        }
      />
      <p>{ value }</p>
    </>
  );
}
0

При использовании debounce важно сохранить оригинальное синтетическое событие с помощью event.persist(). Вот работающий пример, протестированный с React 16+.

import React, { Component } from 'react';
import debounce from 'lodash/debounce';

class ItemType extends Component {
  evntHandler = debounce((e) => {
    console.log(e);
  }, 500);

  render() {
    return (
      <div className="form-field-wrap"
        onClick={e => {
          e.persist();
          this.evntHandler(e);
        }}>
        ...
      </div>
    );
  }
}
export default ItemType;

Для функционального компонента вы можете сделать так:

const Search = ({ getBooks, query }) => {

  const handleOnSubmit = (e) => {
    e.preventDefault();
  }
  
  const debouncedGetBooks = debounce(query => {
    getBooks(query);
  }, 700);

  const onInputChange = e => {
    debouncedGetBooks(e.target.value);
  }

  return (
    <div className="search-books">
      <Form className="search-books--form" onSubmit={handleOnSubmit}>
        <Form.Group controlId="formBasicEmail">
          <Form.Control type="text" onChange={onInputChange} placeholder="Гарри Поттер" />
          <Form.Text className="text-muted">
            Ищите в самом полном индексе текстов книг в мире.
          </Form.Text>
        </Form.Group>
        <Button variant="primary" type="submit">
          Поиск
        </Button>
      </Form>
    </div>
  )
}

Ссылки:

0

Есть пакет use-debounce, который можно использовать с хуками ReactJS.

Вот пример из README пакета:

import { useDebounce } from 'use-debounce';

export default function Input() {
  const [text, setText] = useState('Hello');
  const [value] = useDebounce(text, 1000);

  return (
    <div>
      <input
        defaultValue={'Hello'}
        onChange={(e) => {
          setText(e.target.value);
        }}
      />
      <p>Текущее значение: {text}</p>
      <p>Отложенное значение: {value}</p>
    </div>
  );
}

Как видно из примера выше, переменная value обновляется только один раз в секунду (1000 миллисекунд). Это полезно для оптимизации производительности, особенно при работе с событиями, которые могут быстро срабатывать, например, при вводе текста.

0

Много полезной информации уже представлено, но чтобы быть кратким, вот что работает для меня:

import React, { Component } from 'react';
import _ from 'lodash';

class MyComponent extends Component {
    constructor(props) {
        super(props);
        this.handleChange = _.debounce(this.handleChange.bind(this), 700);
    }
}

В этом коде я использую метод _.debounce из библиотеки Lodash для ограничения частоты вызовов функции handleChange. Это особенно полезно, если у вас есть события, которые могут вызываться слишком часто, например, при вводе текста в поле. Дебаунс поможет улучшить производительность, вызывая функцию только через 700 миллисекунд после того, как событие прекратилось.

0

Ваша реализация выглядит вполне корректной. Вы используете класс UserInput, чтобы хранить состояние ввода пользователя с использованием React и lodash.debounce. Однако есть несколько моментов, которые стоит учесть, чтобы улучшить ваш код и сделать его более "реактивным".

Во-первых, использование refs является старым подходом, и в последних версиях React рекомендуются использовать controlled components. Это означает, что лучше управлять состоянием поля ввода напрямую через состояние компонента, а не обращаться к refs. Вот пример, как это можно сделать:

class UserInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      userInput: ""
    };

    // Привязываем метод к контексту
    this.updateInput = _.debounce(this.updateInput.bind(this), 500);
  }

  updateInput(userInput) {
    this.setState({ userInput });
    // OrderActions.updateValue(userInput); // Выполнение какого-то серверного запроса
  }

  handleChange = (event) => {
    const { value } = event.target;
    this.updateInput(value);
  }

  render() {
    return (
      <div>
        <p>User typed: {this.state.userInput}</p>
        <input onChange={this.handleChange} type="text" />
      </div>
    );
  }
}

ReactDOM.render(<UserInput />, document.getElementById('root'));

В этом коде вместо использования refs мы создали обработчик события handleChange, который принимает событие и извлекает значение из event.target. Затем это значение передается в метод updateInput, который уже будет обрабатывать его с задержкой, установленной с помощью _.debounce.

Такой подход делает ваш компонент более устойчивым и легче читаемым, так как управление состоянием происходит напрямую через React, а не через "обходные пути" с refs.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь