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()
.
debounce
. 여기서, 매번onChange={debounce(this.handleOnChange, 200)}/>
호출debounce function
합니다. 그러나 실제로 필요한 것은 debounce 함수가 반환 한 함수를 호출하는 것입니다.