source

데바운스 어떻게 해요?

nicesource 2022. 10. 15. 08:34
반응형

데바운스 어떻게 해요?

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
  }
});

2019년 : Try Hook + 약속 파기

이것이 제가 이 문제를 해결할 수 있는 가장 최신 버전입니다.다음을 사용합니다.

  • 비동기 기능을 디바운스하기 위한 awesome-communce-module
  • use-disclosed 기능을 컴포넌트에 저장합니다.
  • 결과를 컴포넌트에 입력하기 위한 react-closc-hook

이것은 초기 배선이지만, 사용자가 직접 원시 블록을 구성하고 있으며, 사용자가 직접 사용자 지정 후크를 만들 수 있으므로 이 작업은 한 번만 수행하면 됩니다.

// 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/Undercore)으로 작업하는 것이 불편하고 오류가 발생하기 쉽습니다.API 호출이 임의의 순서로 해결되기 때문에 보일러 플레이트와 동시성 문제가 발생하기 쉽습니다.

React를 염두에 두고 작은 라이브러리를 만들었습니다.대단한 약속입니다.

이것은 그것보다 복잡하지 않습니다.

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 호출이 거부됩니다.
  • 디버깅된 함수는 항상 약속을 반환한다
  • 마지막 콜의 반환된 약속만이 해결됩니다.
  • this.setState({ result }); 콜 발생합니다.

최종적으로 컴포넌트가 마운트 해제되면 다음 트릭을 추가할 수 있습니다.

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

Observatibles(RxJ)는 입력을 비난하는 데에도 적합하지만, 더 강력한 추상화이므로 올바르게 학습/사용하기 어려울 수 있습니다.


< 2017: 콜백 디버깅을 계속 사용하시겠습니까?

여기서 중요한 것은 컴포넌트 인스턴스당 하나의 디버깅(또는 스로틀) 함수를 작성하는 것입니다.데바운스(또는 스로틀) 함수를 매번 재생성할 필요는 없습니다.또한 여러 인스턴스가 같은 디바운스 함수를 공유하지 않도록 해야 합니다.

이 답변에서 디버깅 함수를 정의하는 것은 아닙니다만, 이 답변은 다음과 같이 동작합니다._.debounce사용자 제공 디버깅 기능뿐만 아니라 언더스코어 또는 로더쉬도 사용할 수 있습니다.


좋은 아이디어:

디버깅된 함수는 스테이트풀하기 때문에 디버깅된 함수는 컴포넌트 인스턴스마다 1개씩 작성해야 합니다.

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);
});

작성 시 안돼돼 during during during가 되기 하지 않습니다 왜냐하면 클래스 설명 오브젝트를 만드는 동안this오브젝트 자체가 아닙니다. this.method.thiscontext는 오브젝트 자체가 아닙니다(실제로 아직 작성 중인 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콜백으로 수신한 )는 풀링됩니다(이것에 대해서는 문서화되어 있습니다).즉, 이벤트 콜백이 호출된 후 수신한 SyntheticEvent가 빈 Atribut과 함께 풀에 되돌려져 GC의 압력을 낮춥니다.

이에 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=truepersist를 사용하면 이벤트가 풀에 다시 저장되는 것을 방지할 수 있습니다.

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

Julen의 답변을 읽고 사용 예를 들어보십시오.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);

편집: 예제 및 JSFidles를 React 0.12로 업데이트

편집: Sebastien Lorber가 제기한 문제에 대처하기 위해 업데이트된 예

편집: 언더스코어를 사용하지 않고 플레인 Javascript 디바운스를 사용하는 jsfiddle로 업데이트되었습니다.

2019: 'useCallback' 리액트 후크 사용

해 본 , 저는 이 방법을 사용하고 있는 것을 되었습니다.useCallback를 사용하는 콜의 할 수 .debounce의 an onChange

Hooks 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();
 };

이게 도움이 됐으면 좋겠네요!

한동안 텍스트 입력에 고심하다가 스스로 완벽한 해결책을 찾지 못한 후 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 컴포넌트는 일반 입력 요소에 할당할 수 있는 모든 소품을 받아들입니다.코데펜으로 시험해 보다

다른 사람에게도 도움이 되고 시간도 절약됐으면 좋겠어요.

저스틴 툴크의 이 은 매우 도움이 되었다.리액트/리듀스에서는 보다 공식적인 방법으로 몇 번 시도하면 리액트의 합성 이벤트 풀링에 의해 실패했음을 알 수 있습니다.그런 다음 내부 상태를 사용하여 입력에 변경/입력된 값을 추적하고 직후 콜백을 수행합니다.setState이 명령어는 일부 결과를 실시간으로 보여주는 제한/예고된 감소 액션을 호출합니다.

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}
        />
    )
  }
}

★★★★★★★★★★★★★★★★ 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

이라면 해결 즉, "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} />
    }
}

이 솔루션은 후크 기반(Typescript에 기재)입니다.

훅이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

컴포넌트 범위 내에서 'deverged' 함수를 생성할 뿐입니다.

클릭을 멈춘 지 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;
}

리액트 훅을 사용한 간단한 접근법이 있습니다.

1단계 : 검색된 텍스트를 유지할 상태를 정의합니다.

const [searchTerm, setSearchTerm] = useState('')

2단계 : useEffect를 사용하여 검색어 변경 내용 캡처

useEffect(() => {
  const delayDebounceFn = setTimeout(() => {
    if (searchTerm) {
      // write your logic here
    }
  }, 400)

  return () => clearTimeout(delayDebounceFn)
}, [searchTerm])

3단계 : 입력변경을 처리하는 함수를 작성한다.

function handleInputChange(value) {
  if (value) {
    setSearchTerm(value)
  }
}

이상입니다! 이 메서드를 필요에 따라 호출합니다.

게 요.use-debounce후크에서 사용할 수 패키지입니다.JS »

패키지의 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>
  );
}

에서 알 수 변수 .value1 ( 1000 ) 。

여기엔 이미 많은 좋은 정보들이 있지만, 간결하게 말하자면.난 이거면 되는데...

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★★★★★★★★★★★★★★★★★★:

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에서 이 작업을 수행할 수 있습니다.

ES6 CLASS 및 React 15.x.x & lodash.debounce Im을 사용하면 내부에서 이 바인딩이 손실되므로 여기서 React의 참조를 사용합니다.

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>

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();

도움이 되길 바랍니다.건배!!

참고:

PoC의 다른 실장은 다음과 같습니다.

  • 비난하기 위한 라이브러리(예를 들어 로다시)가 없는
  • 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}
    />
  );
}

2022 - useEffect 후크 사용

때 가장 좋은 은 '''를 하는 것입니다.useEffectuseEffect그럼 비동기 이벤트에 대한 응답으로 상태를 변경할 수 있는 함수를 설정할 수 있습니다.useEffect를 사용하다

후크에서 함수를 반환하면 반환된 함수는 후크가 다시 호출되기 전에 호출됩니다.이것에 의해, 이전의 타임 아웃을 취소해, 사실상 기능을 파기할 수 있습니다.

두 상태가 .value ★★★★★★★★★★★★★★★★★」tempValue ★★tempValue.useEffect으로 1000밀리초 복사를 위한 를 합니다.tempValuevalue.

후크는 타이머 설정을 해제하는 함수를 반환합니다.후크가 다시 호출되면(즉, 다른 키를 누르면) 타임아웃이 취소되고 재설정됩니다.

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

  // This hook will set a 1000 ms timer to copy tempValue into value
  // If the hook is called again, the timer will be cancelled
  // This creates a debounce
  useEffect(
    () => {
      // Wait 1000ms before copying the value of tempValue into value;
      const timeout = setTimeout(() => {
        setValue(tempValue);
      }, 1000);

      // If the hook is called again, cancel the previous timeout
      // This creates a debounce instead of a delay
      return () => clearTimeout(timeout);
    },
    // Run the hook every time the user makes a keystroke
    [tempValue]
  )

  // Here we create an input to set tempValue. 
  // value will be updated 1000 ms after the hook is last called, 
  // i.e after the last user keystroke.
  return (
    <>
      <input 
        onChange={
          ({ target }) => setTempValue(target.value)
        }
      />
      <p>{ value }</p>
    </>
  )
}

이제 2019년 말에 React and 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;

* 이 컴포넌트의 작성자입니다.

이 질문에서는 현재 사용하고 있는 접근법에 대한 답변을 찾을 수 없습니다.이 질문에서는 사용 사례에 가장 적합한 대체 솔루션을 제안하고 싶습니다.

훅를 사용하고 경우 리액트 훅 lib는 리액트 훅입니다.react-use, 훅이라고 하는 useDebounce()꽤 우아한 방식으로 규탄 논리를 실행했습니다.

const [query, setQuery] = useState('');

useDebounce(
  () => {
    emitYourOnDebouncedSearchEvent(query);
  },
  2000,
  [query]
);

return <input onChange={({ currentTarget }) => setQuery(currentTarget.value)} />

자세한 내용은 lib의 github 페이지를 직접 확인하십시오.

https://github.com/streamich/react-use/blob/master/docs/useDebounce.md

2021년 6월부터는 xnimorz 솔루션을 간단하게 구현할 수 있습니다.https://github.com/xnimorz/use-debounce

import { useState, useEffect, useRef } from "react";
// Usage
function App() {
  // State and setters for ...
  // Search term
  const [searchTerm, setSearchTerm] = useState("");
  // API search results
  const [results, setResults] = useState([]);
  // Searching status (whether there is pending API request)
  const [isSearching, setIsSearching] = useState(false);
  // Debounce search term so that it only gives us latest value ...
  // ... if searchTerm has not been updated within last 500ms.
  // The goal is to only have the API call fire when user stops typing ...
  // ... so that we aren't hitting our API rapidly.
  const debouncedSearchTerm = useDebounce(searchTerm, 500);
  // Effect for API call
  useEffect(
    () => {
      if (debouncedSearchTerm) {
        setIsSearching(true);
        searchCharacters(debouncedSearchTerm).then((results) => {
          setIsSearching(false);
          setResults(results);
        });
      } else {
        setResults([]);
        setIsSearching(false);
      }
    },
    [debouncedSearchTerm] // Only call effect if debounced search term changes
  );
  return (
    <div>
      <input
        placeholder="Search Marvel Comics"
        onChange={(e) => setSearchTerm(e.target.value)}
      />
      {isSearching && <div>Searching ...</div>}
      {results.map((result) => (
        <div key={result.id}>
          <h4>{result.title}</h4>
          <img
            src={`${result.thumbnail.path}/portrait_incredible.${result.thumbnail.extension}`}
          />
        </div>
      ))}
    </div>
  );
}
// API search function
function searchCharacters(search) {
  const apiKey = "f9dfb1e8d466d36c27850bedd2047687";
  return fetch(
    `https://gateway.marvel.com/v1/public/comics?apikey=${apiKey}&titleStartsWith=${search}`,
    {
      method: "GET",
    }
  )
    .then((r) => r.json())
    .then((r) => r.data.results)
    .catch((error) => {
      console.error(error);
      return [];
    });
}
// Hook
function useDebounce(value, delay) {
  // State and setters for debounced value
  const [debouncedValue, setDebouncedValue] = useState(value);
  useEffect(
    () => {
      // Update debounced value after delay
      const handler = setTimeout(() => {
        setDebouncedValue(value);
      }, delay);
      // Cancel the timeout if value changes (also on delay change or unmount)
      // This is how we prevent debounced value from updating if value is changed ...
      // .. within the delay period. Timeout gets cleared and restarted.
      return () => {
        clearTimeout(handler);
      };
    },
    [value, delay] // Only re-call effect if value or delay changes
  );
  return debouncedValue;
}

외부 의존이 필요 없는 깔끔한 솔루션:

리액트 훅을 사용한 디버깅

및 useEffectReact를 합니다.setTimeoutclearTimeout★★★★★★ 。

최근 반응과 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)} />)
  }
}

해봤어?

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);
  }
});

handleOnChange를 데바운스()로 랩하는 것이 아니라 데바운스 내의 콜백함수 내에서 에이잭스콜을 랩하여 이벤트개체를 파괴하지 마세요.예를 들어 다음과 같습니다.

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

여기 제가 생각해낸 예시가 있습니다. 다른 클래스를 디바운서로 감싸는 것입니다.데코레이터/고차원으로 만들기에 적합합니다.

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} />
    }
}

여기 @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}
        />
    );
}

을 사용하다다 solved 。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

이 솔루션에서는 추가 lib가 필요하지 않으며 사용자가 Enter 키를 눌렀을 때도 부팅됩니다.

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

const [search, setSearch] = useState('');
const [searchFor, setSearchFor] = useState(search);

useEffect(() => {
    console.log("Search:", searchFor);
}, [searchFor]);

const fireChange = event => {
    const { keyCode } = event;
    if (keyCode === 13) {
        event.preventDefault();
        setSearchFor(search);
    }
}

const changeSearch = event => {
    const { value } = event.target;
    setSearch(value);
    debounceSetSearchFor(value);
};

const debounceSetSearchFor = useCallback(debounce(function(value) {
    setSearchFor(value);
}, 250), []);

입력은 다음과 같습니다.

<input value={search} onKeyDown={fireChange} onChange={changeSearch}  />

후크:

import {useState} from "react";

const useDebounce = ({defaultTimeout = 250, defaultIdentifier = 'default'} = {}) => {

    const [identifiers, setIdentifiers] = useState({[defaultIdentifier]: null});

    return ({fn = null, identifier = defaultIdentifier, timeout = defaultTimeout} = {}) => {
        if (identifiers.hasOwnProperty(identifier)) clearTimeout(identifiers[identifier]);
        setIdentifiers({...identifiers, [identifier]: setTimeout(fn, timeout)});
    };
};

export default useDebounce;

또, 다음과 같은 장소(동일한 파일내의 식별자 사용)에서도 사용할 수 있습니다.

const debounce = useDebounce();

const handlerA = () => {
    debounce({fn: () => console.log('after 2000ms of last call with identifier A'), identifier: 'A', timeout: 2000});
};

const handlerB = () => {
    debounce({fn: () => console.log('after 1500ms of last call with identifier B'), identifier: 'B', timeout: 1500});
};


https://www.npmjs.com/package/use-debouncehttpswww.npmjs.com/package/
use-debounce

import { useDebouncedCallback } from 'use-debounce';

function Input({ defaultValue }) {
  const [value, setValue] = useState(defaultValue);
  const debounced = useDebouncedCallback(
    (value) => {
      setValue(value);
    },
    // delay
    1000
  );

  return (
    <div>
      <input defaultValue={defaultValue} onChange={(e) => debounced(e.target.value)} />
      <p>Debounced value: {value}</p>
    </div>
  );
}

에 대한 로 이 되었는데, 문제가 : 만약 이 이 문제를 .handleOnChange target으로부터의 , 「이벤트 타겟」의 값을 합니다.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)

언급URL : https://stackoverflow.com/questions/23123138/how-to-perform-debounce

반응형