React.js에서 디 바운스 수행


496

React.js에서 디 바운스를 어떻게 수행합니까?

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) {
    // make ajax call
  }
});

나는 당신과 같은 문제를 만났고, 아래의 훌륭한 답변입니다! 그러나 나는 당신이 잘못된 방법을 사용했다고 생각합니다 debounce. 여기서, 매번 onChange={debounce(this.handleOnChange, 200)}/>호출 debounce function합니다. 그러나 실제로 필요한 것은 debounce 함수가 반환 한 함수를 호출하는 것입니다.
pingfengafei

답변:


834

2019 : 훅 + 약속 디 바운싱 시도

이것은이 문제를 해결하는 방법의 최신 버전입니다. 나는 사용할 것이다 :

이것은 초기 배선이지만 자체적으로 기본 블록을 작성하고 있으며 한 번만 수행하면되도록 사용자 정의 후크를 만들 수 있습니다.

// Generic reusable hook
const useDebouncedSearch = (searchFunction) => {

  // Handle the input text state
  const [inputText, setInputText] = useState('');

  // Debounce the original search async function
  const debouncedSearchFunction = useConstant(() =>
    AwesomeDebouncePromise(searchFunction, 300)
  );

  // The async callback is run each time the text changes,
  // but as the search function is debounced, it does not
  // fire a new request on each keystroke
  const searchResults = useAsync(
    async () => {
      if (inputText.length === 0) {
        return [];
      } else {
        return debouncedSearchFunction(inputText);
      }
    },
    [debouncedSearchFunction, inputText]
  );

  // Return everything needed for the hook consumer
  return {
    inputText,
    setInputText,
    searchResults,
  };
};

그런 다음 후크를 사용할 수 있습니다.

const useSearchStarwarsHero = () => useDebouncedSearch(text => searchStarwarsHeroAsync(text))

const SearchStarwarsHeroExample = () => {
  const { inputText, setInputText, searchResults } = useSearchStarwarsHero();
  return (
    <div>
      <input value={inputText} onChange={e => setInputText(e.target.value)} />
      <div>
        {searchResults.loading && <div>...</div>}
        {searchResults.error && <div>Error: {search.error.message}</div>}
        {searchResults.result && (
          <div>
            <div>Results: {search.result.length}</div>
            <ul>
              {searchResults.result.map(hero => (
                <li key={hero.name}>{hero.name}</li>
              ))}
            </ul>
          </div>
        )}
      </div>
    </div>
  );
};

이 예제를 여기에서 실행 하고 자세한 내용 은 react-async-hook 설명서를 읽어야 합니다.


2018 : 약속 철회 시도

우리는 종종 쓸모없는 요청으로 백엔드가 넘치지 않도록 API 호출을 디 바운싱하려고합니다.

2018 년 콜백 (Lodash / Underscore) 작업은 나쁘고 오류가 발생하기 쉽습니다. API 호출이 임의의 순서로 해결되므로 상용구 및 동시성 문제가 발생하기 쉽습니다.

나는 당신의 고통을 해결하기 위해 React를 염두에 둔 작은 라이브러리를 만들었습니다 : awesome-debounce-promise .

이보다 복잡하지 않아야합니다.

const searchAPI = text => fetch('/search?text=' + encodeURIComponent(text));

const searchAPIDebounced = AwesomeDebouncePromise(searchAPI, 500);

class SearchInputAndResults extends React.Component {
  state = {
    text: '',
    results: null,
  };

  handleTextChange = async text => {
    this.setState({ text, results: null });
    const result = await searchAPIDebounced(text);
    this.setState({ result });
  };
}

디 바운스 된 기능은 다음을 보장합니다.

  • API 호출이 디 바운스됩니다
  • debounced 함수는 항상 약속을 반환
  • 마지막 통화의 반환 약속 만 해결됩니다
  • this.setState({ result });API 호출 당 단일 발생

결국 구성 요소가 마운트 해제되면 다른 트릭을 추가 할 수 있습니다.

componentWillUnmount() {
  this.setState = () => {};
}

참고 것을 Observables은 (RxJS)도 입력 디 바운싱을위한 좋은 적합 할 수 있지만, 배우고 더 열심히 할 수있는 더 강력한 추상화입니다 / 올바르게 사용합니다.


<2017 : 여전히 콜백 디 바운싱을 사용 하시겠습니까?

여기서 중요한 부분은 구성 요소 인스턴스 당 단일 디 바운스 된 (또는 스로틀 된) 함수를 작성하는 것 입니다. 매번 디 바운스 (또는 스로틀) 기능을 다시 생성하고 싶지 않으며 여러 인스턴스가 동일한 디 바운스 기능을 공유하는 것을 원하지 않습니다.

이 답변에서 디 바운싱 기능을 실제로 관련이 없기 때문에 정의하지는 않지만이 답변은 _.debounce밑줄 또는 대쉬와 사용자가 제공 한 디 바운싱 기능으로 완벽하게 작동 합니다.


좋은 생각:

디 바운스 된 함수는 상태 저장이므로 컴포넌트 인스턴스 당 하나의 디 바운스 된 함수 를 작성해야 합니다 .

ES6 (클래스 속성) : 권장

class SearchBox extends React.Component {
    method = debounce(() => { 
      ...
    });
}

ES6 (클래스 생성자)

class SearchBox extends React.Component {
    constructor(props) {
        super(props);
        this.method = debounce(this.method.bind(this),1000);
    }
    method() { ... }
}

ES5

var SearchBox = React.createClass({
    method: function() {...},
    componentWillMount: function() {
       this.method = debounce(this.method.bind(this),100);
    },
});

JsFiddle을 참조하십시오 : 3 개의 인스턴스가 인스턴스 당 1 개의 로그 항목을 생성합니다 (전 세계적으로 3 개 생성).


좋은 생각이 아닙니다 :

var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: debounce(this.method, 100);
});

클래스 설명 객체 생성 중에는 this객체 자체가 생성되지 않기 때문에 작동 하지 않습니다. 컨텍스트가 객체 자체가 아니기 this.method때문에 this(실제로 BTW가 실제로 생성되지 않기 때문에) 실제로 존재하지 않기 때문에 원하는 것을 반환 하지 않습니다.


좋은 생각이 아닙니다 :

var SearchBox = React.createClass({
  method: function() {...},
  debouncedMethod: function() {
      var debounced = debounce(this.method,100);
      debounced();
  },
});

이번에는을 호출하는 디 바운스 된 함수를 효과적으로 작성하고 있습니다 this.method. 문제는 모든 debouncedMethod호출에서 다시 생성하므로 새로 생성 된 디 바운스 기능은 이전 호출에 대해 아무것도 모릅니다! 시간이 지남에 따라 동일한 디 바운스 된 기능을 재사용해야합니다. 그렇지 않으면 디 바운싱이 발생하지 않습니다.


좋은 생각이 아닙니다 :

var SearchBox = React.createClass({
  debouncedMethod: debounce(function () {...},100),
});

이것은 약간 까다 롭습니다.

클래스의 탑재 된 모든 인스턴스는 동일한 디 바운스 된 기능을 공유하며 대부분이 원하는 것이 아닙니다!. JsFiddle 참조 . 3 개의 인스턴스가 전 세계적으로 1 개의 로그 항목 만 생성됩니다.

클래스 레벨에서 각 컴포넌트 인스턴스가 공유하는 단일 디 바운싱 된 함수가 아니라 각 컴포넌트 인스턴스에 대해 디 바운스 된 함수를 작성해야 합니다.


React의 이벤트 풀링 관리

우리는 종종 DOM 이벤트를 디 바운싱하거나 조절하기를 원하기 때문에 관련이 있습니다.

React SyntheticEvent에서 콜백에서 수신 한 이벤트 객체 (예 :)는 풀링됩니다 (이제 문서화 됨 ). 이는 이벤트 콜백이 호출 된 후 GC 압력을 줄이기 위해 빈 속성으로 수신 한 SyntheticEvent가 풀에 다시 저장됨을 의미합니다.

따라서 SyntheticEvent스로틀 / 디 바운스의 경우와 같이 원래 콜백에 비동기 적으로 속성에 액세스하면 액세스하는 속성이 지워질 수 있습니다. 이벤트를 풀에 다시 넣지 않으려면 persist()메소드를 사용할 수 있습니다 .

지속하지 않고 (기본 동작 : 풀링 된 이벤트)

onClick = e => {
  alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
  setTimeout(() => {
    alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
  }, 0);
};

hasNativeEvent=false이벤트 속성이 정리되었으므로 두 번째 (비동기)가 인쇄 됩니다.

지속

onClick = e => {
  e.persist();
  alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
  setTimeout(() => {
    alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
  }, 0);
};

이벤트를 풀에 다시 넣는 것을 피할 수 hasNativeEvent=true있기 때문에 두 번째 (비동기)가 인쇄됩니다 persist.

여기서 두 가지 동작을 테스트 할 수 있습니다. JsFiddle

스로틀 / 디 바운스 기능과 함께 사용하는 예는 Julen의 답변 을 읽으십시오 persist().


3
최고의 대답은,이 제출하거나 및 onblur 양식을 취소 할 수있는 다음, 그들은 입력을 중단 한 후 몇 초 동안 '상호 작용'으로 양식 필드 상태를 설정하고 위해 중대하다
arush_try.com

8
ES6에서는 생성자 내에서 메서드를 정의하는 대신 (이상한 느낌) handleOnChange = debounce((e) => { /* onChange handler code here */ }, timeout)클래스의 최상위 수준에서 수행 할 수 있습니다 . 여전히 인스턴스 멤버를 효과적으로 설정하고 있지만 일반적인 메소드 정의와 비슷합니다. constructor아직 정의하지 않은 경우 에는 필요 하지 않습니다. 나는 그것이 스타일 선호가 대부분이라고 가정합니다.
thom_nic

24
디 바운스 된 메소드를 취소하는 것을 잊지 마십시오 componentWillUnmount: this.method.cancel()-그렇지 않으면 마운트 해제 된 구성 요소에서 state를 설정하려고 할 수 있습니다.
elado

4
@JonasKello 당신은 디 바운스 된 함수가 실제로 스테이트 풀 (stateful)하기 때문에 스테이트리스 컴포넌트 안에서 디 바운싱 할 수 없습니다. 디 바운스 된 함수를 보유하려면 상태 저장 컴포넌트가 필요하지만, 필요한 경우 이미 디 바운스 된 함수를 사용하여 상태 비 저장 컴포넌트를 호출 할 수 있습니다.
Sebastien Lorber

2
왜 모든 답변에 함수를 작성하는 대신 _.debounce가 포함되어 있습니까? 해당 기능에 대한 전체 라이브러리가 필요합니까?
chifliiiii

217

제어되지 않은 구성 요소

event.persist()방법을 사용할 수 있습니다 .

밑줄을 사용한 예는 다음과 같습니다 _.debounce().

var SearchBox = React.createClass({

  componentWillMount: function () {
     this.delayedCallback = _.debounce(function (event) {
       // `event.target` is accessible now
     }, 1000);
  },

  onChange: function (event) {
    event.persist();
    this.delayedCallback(event);
  },

  render: function () {
    return (
      <input type="search" onChange={this.onChange} />
    );
  }

});

편집 : 이 JSFiddle을 참조하십시오


제어 부품

업데이트 : 위의 예는 제어되지 않은 구성 요소를 보여줍니다 . 제어 요소를 항상 사용하므로 위의 또 다른 예가 있지만 event.persist()"속임수" 는 사용하지 않습니다 .

JSFiddle 사용할 수 있습니다 뿐만 아니라. 밑줄없는 예

var SearchBox = React.createClass({
    getInitialState: function () {
        return {
            query: this.props.query
        };
    },

    componentWillMount: function () {
       this.handleSearchDebounced = _.debounce(function () {
           this.props.handleSearch.apply(this, [this.state.query]);
       }, 500);
    },

    onChange: function (event) {
      this.setState({query: event.target.value});
      this.handleSearchDebounced();
    },

    render: function () {
      return (
        <input type="search"
               value={this.state.query}
               onChange={this.onChange} />
      );
    }
});


var Search = React.createClass({
    getInitialState: function () {
        return {
            result: this.props.query
        };
    },

    handleSearch: function (query) {
        this.setState({result: query});
    },

    render: function () {
      return (
        <div id="search">
          <SearchBox query={this.state.result}
                     handleSearch={this.handleSearch} />
          <p>You searched for: <strong>{this.state.result}</strong></p>
        </div>
      );
    }
});

React.render(<Search query="Initial query" />, document.body);

편집 : 0.12에 반응하도록 업데이트 된 예제 및 JSFiddles

편집 : Sebastien Lorber가 제기 한 문제를 해결하기 위해 업데이트 된 예

편집 : 밑줄을 사용하지 않고 일반 자바 스크립트 디 바운스를 사용하는 jsfiddle로 업데이트되었습니다.


입력에는 작동하지 않습니다. 디 바운스 된 함수의 이벤트 대상에 더 이상 값이 없으므로 입력이 비어 있습니다.
Etai

1
약간 복잡합니다. 소품에 대해 조심해야합니다. 설정하면 <input value={this.props.someprop}...키를 누를 때 업데이트가 디 바운스가 끝날 때까지 구성 요소로 다시 업데이트되지 않으므로 제대로 렌더링되지 않습니다. 이것이 value=관리되지 않은 것에 대해 행복하다면 생략하는 것이 좋지만, 값을 미리 채우거나 다른 곳에 묶고 싶다면 분명히 작동하지 않습니다.
Alastair Maw

1
@AlastairMaw는 질문에 통제되지 않은 구성 요소가 있었기 때문에 답장도 있습니다. 미리 채워진 값으로 제어되는 구성 요소의 대체 버전을 아래에 추가했습니다.
julen

2
DOM에 컴포넌트 뮤티 플 타임을 마운트 할 경우 이는 매우 위험합니다. stackoverflow.com/questions/23123138/…
Sebastien Lorber

4
이것은 훌륭한 답변이지만 persiston과 같은 많은 이벤트가있을 때 특히 사용하지 않는 것이 좋습니다 mousemove. 그런 식으로 코드가 완전히 응답하지 않는 것을 보았습니다. 이벤트 호출에서 기본 이벤트에서 필요한 데이터를 추출한 다음 이벤트 자체가 아닌 데이터만으로 디 바운스 / 스로틀 기능을 호출하는 것이 훨씬 효율적입니다. 그런 식으로 이벤트를 유지할 필요가 없습니다
MrE

31

2019 : 'useCallback'반응 후크 사용

많은 다른 접근 방식을 시도한 후 이벤트 내에서 useCallback사용하는 다중 호출 문제를 해결하는 데 가장 간단하고 효율적인 방법을 사용 했습니다.debounceonChange

당으로 후크 API 문서 ,

useCallback은 의존성 중 하나가 변경된 경우에만 변경되는 기억 된 버전의 콜백을 반환합니다.

빈 배열을 종속성으로 전달하면 콜백이 한 번만 호출됩니다. 간단한 구현은 다음과 같습니다.

import React, { useCallback } from "react";
import { debounce } from "lodash";

const handler = useCallback(debounce(someFunction, 2000), []);

const onChange = (event) => {
    // perform any event related action here

    handler();
 };

도움이 되었기를 바랍니다!


3
후크를 사용하는 경우 탁월한 솔루션입니다. 당신은 더 많은 시간의 좌절을 구했습니다. 감사!
칼 에드워즈

여러 통화가 먼저 발생하는 이유를 설명해 주시겠습니까? 않습니다 debounce()고려하지 onChange()같은 콜백 방법으로 콜백을?
El Anonimo 2014

내 앱에서 작동하도록이 솔루션을 수정했습니다. 먼저 const testFunc2 = useCallback(debounce((text) => console.log('testFunc2() has ran:', text), 1000) , []);함수 구성 요소의 본문 내 에서 줄을 이동해야했습니다. 그렇지 않으면 React에서 후크 사용에 대한 오류 메시지를 출력합니다. 그런 다음 onChange이벤트 핸들러에서 : <input type='text' name='name' className='th-input-container__input' onChange={evt => {testFunc2(evt.target.value);}}.
El Anonimo '16

다음은이 솔루션을 사용하여 사용자가 입력을 입력 한 다음 입력이 완료되면 입력 값으로 디 바운스 된 API 호출을 보내는 방법입니다. stackoverflow.com/questions/59358092/… .
El Anonimo

14

Justin Tulk 의이 게시물이 매우 유용 하다는 것을 알았 습니다 . 두 번의 시도 후, 반응 / 환원에 대한보다 공식적인 방법으로 인식되는 것은 React의 합성 이벤트 풀링 으로 인해 실패했음을 보여줍니다 . 그런 다음 그의 솔루션은 일부 내부 상태를 사용하여 입력에서 변경 / 입력 된 값을 추적하며, setState그 직후 콜백을 통해 일부 결과를 실시간으로 보여주는 스로틀 / 디 바운스 된 redux 작업을 호출합니다.

import React, {Component} from 'react'
import TextField from 'material-ui/TextField'
import { debounce } from 'lodash'

class TableSearch extends Component {

  constructor(props){
    super(props)

    this.state = {
        value: props.value
    }

    this.changeSearch = debounce(this.props.changeSearch, 250)
  }

  handleChange = (e) => {
    const val = e.target.value

    this.setState({ value: val }, () => {
      this.changeSearch(val)
    })
  }

  render() {

    return (
        <TextField
            className = {styles.field}
            onChange = {this.handleChange}
            value = {this.props.value}
        />
    )
  }
}

14

이벤트 객체에서 필요한 것이 DOM 입력 요소를 얻는 것이라면 솔루션이 훨씬 간단합니다 ref. 여기에는 밑줄 이 필요합니다 .

class Item extends React.Component {
    constructor(props) {
        super(props);
        this.saveTitle = _.throttle(this.saveTitle.bind(this), 1000);
    }
    saveTitle(){
        let val = this.inputTitle.value;
        // make the ajax call
    }
    render() {
        return <input 
                    ref={ el => this.inputTitle = el } 
                    type="text" 
                    defaultValue={this.props.title} 
                    onChange={this.saveTitle} />
    }
}

2
defaultValue는 내가 원하는 것입니다! 매우 감사합니다 :)
Tazo leladze

14

잠시 동안 텍스트 입력으로 어려움을 겪고 나 자신에게 완벽한 솔루션을 찾지 못하면 npm에서 react-debounce-input을 발견했습니다 .

다음은 간단한 예입니다.

import React from 'react';
import ReactDOM from 'react-dom';
import {DebounceInput} from 'react-debounce-input';

class App extends React.Component {
state = {
    value: ''
};

render() {
    return (
    <div>
        <DebounceInput
        minLength={2}
        debounceTimeout={300}
        onChange={event => this.setState({value: event.target.value})} />

        <p>Value: {this.state.value}</p>
    </div>
    );
}
}

const appRoot = document.createElement('div');
document.body.appendChild(appRoot);
ReactDOM.render(<App />, appRoot);

DebounceInput 컴포넌트는 일반 입력 요소에 지정할 수있는 모든 소품을 받아들입니다. 코드 펜 에서 사용해보십시오

다른 사람도 도와주고 시간을 절약하기를 바랍니다.


여기에 나열된 많은 솔루션을 시도한 후 가장 쉬운 방법이었습니다.
Vadorequest

9

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="Harry Potter" />
          <Form.Text className="text-muted">
            Search the world's most comprehensive index of full-text books.
          </Form.Text>
        </Form.Group>
        <Button variant="primary" type="submit">
          Search
        </Button>
      </Form>
    </div>
  )
}

참조--https: //gist.github.com/elijahmanor/08fc6c8468c994c844213e4a4344a709-https : //blog.revathskumar.com/2016/02/reactjs-using-debounce-in-react-components.html


1
내가 지금까지 찾은 최고의 최상의 구현 방법
Vincent Tang

8

redux를 사용하는 경우 미들웨어로 매우 우아한 방법 으로이 작업을 수행 할 수 있습니다. Debounce미들웨어를 다음과 같이 정의 할 수 있습니다 .

var timeout;
export default store => next => action => {
  const { meta = {} } = action;
  if(meta.debounce){
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      next(action)
    }, meta.debounce)
  }else{
    next(action)
  }
}

그런 다음 다음과 같이 액션 생성자에 디 바운싱을 추가 할 수 있습니다.

export default debouncedAction = (payload) => ({
  type : 'DEBOUNCED_ACTION',
  payload : payload,
  meta : {debounce : 300}
}

실제로 당신을 위해 이것을하기 위해 npm에서 벗어날 수있는 미들웨어이미 있습니다.


이 미들웨어는 applyMiddleware(...)우리가 많은 경우 체인 에서 가장 먼저 실행되어야한다고 생각합니다
Youssef

시간 초과가 초기화되지 않았으며 첫 번째 clearTimeout은 매개 변수에 대해 정의되지 않은 상태로 처리됩니다. 안좋다.
Jason Rice

7

ES6 클래스를 사용하고 15.xx 반작용 의 반작용 사용 및 lodash.debounce 임 심판을 내부적으로이 바인드 이벤트 손실 때문에 여기.

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


  updateInput(userInput) {
    this.setState({
      userInput
    });
    //OrderActions.updateValue(userInput);//do some server stuff
  }


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

ReactDOM.render( <
  UserInput / > ,
  document.getElementById('root')
);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.5/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>


<div id="root"></div>


7

여기에 좋은 정보가 많이 있지만 간결해야합니다. 이것은 나를 위해 작동합니다 ...

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 래퍼를 제거 하면 작동합니다. 그래도 나는이 아이디어를 좋아한다!
Mote Zart

나는 여기에 많은 것을 제공하기 위해 귀하의 코드를 볼 필요가 있지만, 다른 일이 있다고 생각합니다 ...이 훨씬 더 철저한 답변이 약간의 빛을 비추 길 바랍니다. stackoverflow.com/questions/23123138/…
chad steele

6

Lodash debounce https://lodash.com/docs/4.17.5#debounce 방법을 사용할 수 있습니다 . 간단하고 효과적입니다.

import * as lodash from lodash;

const update = (input) => {
    // Update the input here.
    console.log(`Input ${input}`);     
}

const debounceHandleUpdate = lodash.debounce((input) => update(input), 200, {maxWait: 200});

doHandleChange() {
   debounceHandleUpdate(input);
}

아래 방법을 사용하여 디 바운스 방법을 취소 할 수도 있습니다.

this.debounceHandleUpdate.cancel();

그것이 당신을 돕기를 바랍니다. 건배!!


5

참고로

다른 PoC 구현은 다음과 같습니다.

  • 탈퇴를위한 라이브러리 (예 : lodash)가없는 경우
  • React Hooks API 사용

나는 그것이 도움이되기를 바랍니다 :)

import React, { useState, useEffect, ChangeEvent } from 'react';

export default function DebouncedSearchBox({
  inputType,
  handleSearch,
  placeholder,
  debounceInterval,
}: {
  inputType?: string;
  handleSearch: (q: string) => void;
  placeholder: string;
  debounceInterval: number;
}) {
  const [query, setQuery] = useState<string>('');
  const [timer, setTimer] = useState<NodeJS.Timer | undefined>();

  useEffect(() => {
    if (timer) {
      clearTimeout(timer);
    }
    setTimer(setTimeout(() => {
      handleSearch(query);
    }, debounceInterval));
  }, [query]);

  const handleOnChange = (e: ChangeEvent<HTMLInputElement>): void => {
    setQuery(e.target.value);
  };

  return (
    <input
      type={inputType || 'text'}
      className="form-control"
      placeholder={placeholder}
      value={query}
      onChange={handleOnChange}
    />
  );
}

4

use-debounceReactJS 후크와 함께 사용할 수 있는 패키지가 있습니다.

패키지의 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>Actual value: {text}</p>
      <p>Debounce value: {value}</p>
    </div>
  );
}

위의 예에서 볼 수 있듯이 변수 value는 1 초에 한 번 (1000 밀리 초) 만 업데이트하도록 설정되어 있습니다.


3

최근 반응과 lodash를 가진 또 다른 변형.

class Filter extends Component {
  static propTypes = {
    text: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired
  }

  state = {
    initialText: '',
    text: ''
  }

  constructor (props) {
    super(props)

    this.setText = this.setText.bind(this)
    this.onChange = _.fp.debounce(500)(this.onChange.bind(this))
  }

  static getDerivedStateFromProps (nextProps, prevState) {
    const { text } = nextProps

    if (text !== prevState.initialText) {
      return { initialText: text, text }
    }

    return null
  }

  setText (text) {
    this.setState({ text })
    this.onChange(text)
  }

  onChange (text) {
    this.props.onChange(text)
  }

  render () {
    return (<input value={this.state.text} onChange={(event) => this.setText(event.target.value)} />)
  }
}


3

시도해 보았 니?

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) {
    debounce(\\ Your handleChange code , 200);
  }
});

2

debounce ()에 handleOnChange를 래핑하는 대신, debounce 내의 콜백 함수 안에 ajax 호출을 래핑하여 이벤트 객체를 파괴하지 않는 이유는 무엇입니까? 그래서 이런 식으로 :

handleOnChange: function (event) {
   debounce(
     $.ajax({})
  , 250);
}

4
이벤트 객체는 변경할 수 없으며 ReactJS에 의해 파괴되므로 클로즈 캡처를 래핑하고 획득하더라도 코드는 실패합니다.
Henrik

2

다음은 디 바운서로 다른 클래스를 감싸는 예제입니다. 이것은 데코레이터 / 고차 함수로 만들어지는 데 적합합니다.

export class DebouncedThingy extends React.Component {
    static ToDebounce = ['someProp', 'someProp2'];
    constructor(props) {
        super(props);
        this.state = {};
    }
    // On prop maybe changed
    componentWillReceiveProps = (nextProps) => {
        this.debouncedSetState();
    };
    // Before initial render
    componentWillMount = () => {
        // Set state then debounce it from here on out (consider using _.throttle)
        this.debouncedSetState();
        this.debouncedSetState = _.debounce(this.debouncedSetState, 300);
    };
    debouncedSetState = () => {
        this.setState(_.pick(this.props, DebouncedThingy.ToDebounce));
    };
    render() {
        const restOfProps = _.omit(this.props, DebouncedThingy.ToDebounce);
        return <Thingy {...restOfProps} {...this.state} />
    }
}

2

2019 년 말 에 React 및 React Native에 대한 또 다른 솔루션이 있습니다 .

반응 디 바운스 구성 요소

<input>
<Debounce ms={500}>
  <List/>
</Debounce>

사용하기 쉽고 작고 widley 지원되는 구성 요소입니다.

예:

여기에 이미지 설명을 입력하십시오

import React from 'react';
import Debounce from 'react-debounce-component';

class App extends React.Component {
  constructor (props) {
    super(props);
    this.state = {value: 'Hello'}
  }
  render () {
    return (
      <div>
        <input value={this.state.value} onChange={(event) => {this.setState({value: event.target.value})}}/>
        <Debounce ms={1000}>
          <div>{this.state.value}</div>
        </Debounce>
      </div>
    );
  }
}

export default App;

*이 구성 요소의 제작자입니다


1

저도 같은 문제에 대한 해결책을 찾고,이 스레드뿐만 아니라 몇몇 다른 사람을 통해 들어 왔지만 그들은 같은 문제가 있었다 : 당신이 일을하려고하는 경우 handleOnChange기능을하고 이벤트 대상에서 값을해야합니다, 당신은 얻을 것이다 cannot read property value of null일부 또는 그러한 오류. 필자의 경우 this플럭스 블 액션을 실행하기 때문에 디 바운스 된 함수 내부의 컨텍스트를 유지해야했습니다 . 여기 내 솔루션이 있습니다. 사용 사례에 잘 작동하므로 누군가이 스레드를 발견 할 경우를 대비하여 여기에 남겨 두겠습니다.

// at top of file:
var myAction = require('../actions/someAction');

// inside React.createClass({...});

handleOnChange: function (event) {
    var value = event.target.value;
    var doAction = _.curry(this.context.executeAction, 2);

    // only one parameter gets passed into the curried function,
    // so the function passed as the first parameter to _.curry()
    // will not be executed until the second parameter is passed
    // which happens in the next function that is wrapped in _.debounce()
    debouncedOnChange(doAction(myAction), value);
},

debouncedOnChange: _.debounce(function(action, value) {
    action(value);
}, 300)

1

에 대한 throttle또는 debounce가장 좋은 방법은 당신이 어떤 사용할 수 있도록하는 기능 크리에이터를 만드는 것입니다 경우, 예를 들어 :

  updateUserProfileField(fieldName) {
    const handler = throttle(value => {
      console.log(fieldName, value);
    }, 400);
    return evt => handler(evt.target.value.trim());
  }

그리고 당신의 render방법으로 할 수 있습니다 :

<input onChange={this.updateUserProfileField("givenName").bind(this)}/>

그만큼 updateUserProfileField 메소드는 호출 할 때마다 분리 된 함수를 작성합니다.

참고 처리기를 직접 반환하려고 시도하지 마십시오. 예를 들어 작동하지 않습니다.

 updateUserProfileField(fieldName) {
    return evt => throttle(value => {
      console.log(fieldName, value);
    }, 400)(evt.target.value.trim());
  }

동일한 스로틀 함수를 사용하는 대신 이벤트가 호출 될 때마다 새로운 스로틀 함수를 생성하기 때문에 이것이 작동하지 않는 이유는 기본적으로 스로틀은 쓸모가 없습니다.)

또한 당신이 사용 debounce하거나 throttle필요하지 않은 setTimeout또는 clearTimeout, 이것이 실제로 우리가 그것들을 사용하는 이유입니다 : P


1

다음은 함수 구성 요소에 싸인 @Abra의 접근 방식을 사용하는 스 니펫입니다 (UI에는 패브릭을 사용하고 간단한 버튼으로 교체하십시오).

import React, { useCallback } from "react";
import { debounce } from "lodash";

import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';

const debounceTimeInMS = 2000;

export const PrimaryButtonDebounced = (props) => {

    const debouncedOnClick = debounce(props.onClick, debounceTimeInMS, { leading: true });

    const clickHandlerDebounced = useCallback((e, value) => {

        debouncedOnClick(e, value);

    },[]);

    const onClick = (e, value) => {

        clickHandlerDebounced(e, value);
    };

    return (
        <PrimaryButton {...props}
            onClick={onClick}
        />
    );
}

1

내 솔루션은 후크 기반입니다 (Typescript로 작성 됨).

메인 훅이 2 개 useDebouncedValue있고useDebouncedCallback

먼저- useDebouncedValue

검색 창이 있다고 가정하지만 사용자가 0.5 초 동안 입력을 중지 한 후 서버에 검색 결과를 요청하려고합니다.

function SearchInput() {
  const [realTimeValue, setRealTimeValue] = useState('');

  const debouncedValue = useDebouncedValue(realTimeValue, 500); // this value will pick real time value, but will change it's result only when it's seattled for 500ms

  useEffect(() => {
    // this effect will be called on seattled values
    api.fetchSearchResults(debouncedValue);
  }, [debouncedValue])

  return <input onChange={event => setRealTimeValue(event.target.value)} />
}

이행

import { useState, useEffect } from "react";

export function useDebouncedValue<T>(input: T, time = 500) {
  const [debouncedValue, setDebouncedValue] = useState(input);

  // every time input value has changed - set interval before it's actually commited
  useEffect(() => {
    const timeout = setTimeout(() => {
      setDebouncedValue(input);
    }, time);

    return () => {
      clearTimeout(timeout);
    };
  }, [input, time]);

  return debouncedValue;
}

둘째 useDebouncedCallback

구성 요소의 범위에서 '파손 된'기능을 만듭니다.

클릭을 멈 추면 500ms 경고를 표시하는 버튼이있는 구성 요소가 있다고 가정 해 봅시다.

function AlertButton() {
  function showAlert() {
    alert('Clicking has seattled');
  }

  const debouncedShowAlert = useDebouncedCallback(showAlert, 500);

  return <button onClick={debouncedShowAlert}>Click</button>
}

구현 (참고 : lodash / debounce를 도우미로 사용하고 있음)

import debounce from 'lodash/debounce';
import { useMemo } from 'react';

export function useDebouncedCallback<T extends (...args: any) => any>(callback: T, wait?: number) {
  const debouncedCallback = useMemo(() => debounce(callback, wait), [callback, wait]);

  return debouncedCallback;
}

0

다음은 TS를 사용하고 async함수 를 디 바운스하려는 사람들을위한 유효한 TypeScript 예제입니다 .

function debounce<T extends (...args: any[]) => any>(time: number, func: T): (...funcArgs: Parameters<T>) => Promise<ReturnType<T>> {
     let timeout: Timeout;

     return (...args: Parameters<T>): Promise<ReturnType<T>> => new Promise((resolve) => {
         clearTimeout(timeout);
         timeout = setTimeout(() => {
             resolve(func(...args));
         }, time)
     });
 }

0

여기서 조금 늦었지만 도움이 될 것입니다. 이 클래스를 작성하십시오 (typescript로 작성되었지만 Javascript로 쉽게 변환 할 수 있음)

export class debouncedMethod<T>{
  constructor(method:T, debounceTime:number){
    this._method = method;
    this._debounceTime = debounceTime;
  }
  private _method:T;
  private _timeout:number;
  private _debounceTime:number;
  public invoke:T = ((...args:any[])=>{
    this._timeout && window.clearTimeout(this._timeout);
    this._timeout = window.setTimeout(()=>{
      (this._method as any)(...args);
    },this._debounceTime);
  }) as any;
}

그리고 사용하기

var foo = new debouncedMethod((name,age)=>{
 console.log(name,age);
},500);
foo.invoke("john",31);

0

tlence tlence를 사용할 수 있습니다

function log(server) {
  console.log('connecting to', server);
}

const debounceLog = debounce(log, 5000);
// just run last call to 5s
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');
debounceLog('local');

0

Julen 솔루션은 읽기가 어렵습니다. 여기서 제목에 기초하여 질문을 비틀었던 사람을위한 명확하고 완벽한 반응 코드가 있습니다.

tl; dr version : 옵저버로 업데이트 할 때 대신 일정 메서드를 호출하면 실제로 옵저버에게 알리거나 실제로 아약스를 수행합니다.

예제 컴포넌트 jsfiddle로 완전한 jsfiddle

var InputField = React.createClass({

    getDefaultProps: function () {
        return {
            initialValue: '',
            onChange: null
        };
    },

    getInitialState: function () {
        return {
            value: this.props.initialValue
        };
    },

    render: function () {
        var state = this.state;
        return (
            <input type="text"
                   value={state.value}
                   onChange={this.onVolatileChange} />
        );
    },

    onVolatileChange: function (event) {
        this.setState({ 
            value: event.target.value 
        });

        this.scheduleChange();
    },

    scheduleChange: _.debounce(function () {
        this.onChange();
    }, 250),

    onChange: function () {
        var props = this.props;
        if (props.onChange != null) {
            props.onChange.call(this, this.state.value)
        }
    },

});

3
이것이 클래스 정의로 만들어 졌기 때문에 InputField의 모든 인스턴스에서 디 바운스의 상태 / 타이밍을 전역으로 만들지 않습니까? 어쩌면 그것은 당신이 원하는 것일 수도 있지만, 상관없이 주목할 가치가 있습니다.
robbles

1
돔에 여러 번 장착 된 경우 위험합니다. stackoverflow.com/questions/23123138/…
Sebastien Lorber

2
이중 마운트 문제로 인해 잘못된 솔루션입니다. 싱글 톤 일정을 예약하는 기능을 만드는 것은 좋지 않습니다. -1
Henrik

0

사용하지 마십시오 event.persist()-React가 합성 이벤트를 재활용하도록하려고합니다. 클래스를 사용하든 후크를 사용하든 가장 깨끗한 방법은 콜백을 두 조각으로 나누는 것입니다.

  1. 수신 거부가없는 콜백
  2. 필요한 이벤트 만으로 디 바운스 된 함수를 호출 합니다 (따라서 합성 이벤트를 재활용 할 수 있음)

클래스

handleMouseOver = throttle(target => {
  console.log(target);
}, 1000);

onMouseOver = e => {
  this.handleMouseOver(e.target);
};

<div onMouseOver={this.onMouseOver} />

기능

const handleMouseOver = useRef(throttle(target => {
  console.log(target);
}, 1000));

function onMouseOver(e) {
  handleMouseOver.current(e.target);
}

<div onMouseOver={this.onMouseOver} />

handleMouseOver함수가 구성 요소 내에서 상태를 사용 하는 경우 useMemo대신 useRef의존성으로 사용 하고 전달 해야합니다. 그렇지 않으면 오래된 데이터로 작업 할 것입니다 (물론 클래스에는 적용되지 않음).


0

useState 훅 확장

import { useState } from "react";
import _ from "underscore"
export const useDebouncedState = (initialState, durationInMs = 500) => {
    const [internalState, setInternalState] = useState(initialState);
    const debouncedFunction = _.debounce(setInternalState, durationInMs);
    return [internalState, debouncedFunction];
};
export default useDebouncedState;

후크 사용

import useDebouncedState from "../hooks/useDebouncedState"
//...
const [usernameFilter, setUsernameFilter] = useDebouncedState("")
//...
<input id="username" type="text" onChange={e => setUsernameFilter(e.target.value)}></input>

https://trippingoncode.com/react-debounce-hook/


0

오늘이 문제를 만났습니다. 사용하여 해결 setTimeout하고 clearTimeout.

나는 당신이 적응할 수있는 예를 줄 것이다 :

import React, { Component } from 'react'

const DEBOUNCE_TIME = 500

class PlacesAutocomplete extends Component {
  debounceTimer = null;

  onChangeHandler = (event) => {
    // Clear the last registered timer for the function
    clearTimeout(this.debounceTimer);

    // Set a new timer
    this.debounceTimer = setTimeout(
      // Bind the callback function to pass the current input value as arg
      this.getSuggestions.bind(null, event.target.value), 
      DEBOUNCE_TIME
    )
  }

  // The function that is being debounced
  getSuggestions = (searchTerm) => {
    console.log(searchTerm)
  }

  render() {
    return (
      <input type="text" onChange={this.onChangeHandler} />
    )
  }
}

export default PlacesAutocomplete

자체 함수 구성 요소에서 리팩터링 할 수도 있습니다.

import React from 'react'

function DebouncedInput({ debounceTime, callback}) {
  let debounceTimer = null
  return (
    <input type="text" onChange={(event) => {
      clearTimeout(debounceTimer);

      debounceTimer = setTimeout(
        callback.bind(null, event.target.value), 
        debounceTime
      )
    }} />
  )
}

export default DebouncedInput

그리고 그것을 다음과 같이 사용하십시오 :

import React, { Component } from 'react'
import DebouncedInput from '../DebouncedInput';

class PlacesAutocomplete extends Component {
  debounceTimer = null;

  getSuggestions = (searchTerm) => {
    console.log(searchTerm)
  }

  render() {
    return (
      <DebouncedInput debounceTime={500} callback={this.getSuggestions} />
    )
  }
}

export default PlacesAutocomplete
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.