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 : 훅 + 약속 디 바운싱 시도
이것은이 문제를 해결하는 방법의 최신 버전입니다. 나는 사용할 것이다 :
- 비동기 기능을 디 바운스하는 멋진 디 바운스 약속
- 그 디 바운스 된 기능을 구성 요소에 저장하는 데 사용 상수
- react-async-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 / 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()
.
답변
제어되지 않은 구성 요소
이 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로 업데이트되었습니다.
답변
2019 : ‘useCallback’반응 후크 사용
많은 다른 접근 방식을 시도한 후 이벤트 내에서 useCallback
사용하는 다중 호출 문제를 해결하는 데 가장 간단하고 효율적인 방법을 사용 했습니다.debounce
onChange
당으로 후크 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();
};
도움이 되었기를 바랍니다!
답변
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}
/>
)
}
}
답변
이벤트 객체에서 필요한 것이 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} />
}
}
답변
잠시 동안 텍스트 입력으로 어려움을 겪고 나 자신에게 완벽한 솔루션을 찾지 못하면 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 컴포넌트는 일반 입력 요소에 지정할 수있는 모든 소품을 받아들입니다. 코드 펜 에서 사용해보십시오
다른 사람도 도와주고 시간을 절약하기를 바랍니다.
답변
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