२०१ ९: हुक + वाद-विवाद का प्रयास करें
यह अब तक का सबसे संस्करण है कि मैं इस समस्या को कैसे हल करूंगा। मै इस्तेमाल करूंगा:
यह कुछ प्रारंभिक वायरिंग है लेकिन आप अपने दम पर आदिम ब्लॉकों की रचना कर रहे हैं, और आप अपने स्वयं के कस्टम हुक बना सकते हैं ताकि आपको केवल एक बार ऐसा करने की आवश्यकता हो।
// 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>
);
};
आपको यह उदाहरण यहां चल रहा है और आपको अधिक विवरण के लिए अभिक्रिया- async- हुक प्रलेखन पढ़ना चाहिए ।
2018: वाद-विवाद वाद-विवाद का प्रयास करें
हम अक्सर बेकार अनुरोधों के साथ बैकएंड को बाढ़ से बचने के लिए एपीआई कॉल की बहस करना चाहते हैं।
2018 में, कॉलबैक (लॉडश / अंडरस्कोर) के साथ काम करना मुझे बुरा और त्रुटि-रहित लगता है। एक अनियंत्रित क्रम में एपीआई कॉल का समाधान होने के कारण बॉयलरप्लेट और कॉन्सिक्वेंसी मुद्दों का सामना करना आसान है।
मैंने आपके दर्द को हल करने के लिए रिएक्ट के साथ एक छोटी सी लाइब्रेरी बनाई है: भयानक-बहस-वादा ।
इससे अधिक जटिल नहीं होना चाहिए:
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 });
};
}
विवादित फ़ंक्शन यह सुनिश्चित करता है कि:
- एपीआई कॉल पर बहस की जाएगी
- विवादास्पद फ़ंक्शन हमेशा एक वादा लौटाता है
- केवल अंतिम कॉल का दिया गया वादा हल होगा
- एक
this.setState({ result });
एपीआई प्रति कॉल के साथ होगा
अंततः, यदि आपका घटक अनमाउंट करता है, तो आप एक और ट्रिक जोड़ सकते हैं:
componentWillUnmount() {
this.setState = () => {};
}
ध्यान दें कि observables / सही ढंग से उपयोग करें (RxJS) भी आदानों debouncing के लिए बिल्कुल उपयुक्त हो सकता है, लेकिन यह एक अधिक शक्तिशाली अमूर्त जो कठिन जानने के लिए किया जा सकता है है।
<2017: अभी भी कॉलबैक डिबॉन्डिंग का उपयोग करना चाहते हैं?
यहाँ महत्वपूर्ण हिस्सा प्रति घटक उदाहरण के लिए एक एकल डेबिट (या थ्रॉटल) फ़ंक्शन बनाना है । आप हर बार डेबिट (या थ्रोटल) फ़ंक्शन को फिर से बनाना नहीं चाहते हैं, और आप एक ही डेबिट फ़ंक्शन को साझा करने के लिए या तो कई उदाहरण नहीं चाहते हैं।
मैं इस उत्तर में एक डिबगिंग फ़ंक्शन को परिभाषित नहीं कर रहा हूं क्योंकि यह वास्तव में प्रासंगिक नहीं है, लेकिन यह उत्तर _.debounce
अंडरस्कोर या लॉश के साथ-साथ किसी भी उपयोगकर्ता द्वारा प्रदान किए गए डिबेंचिंग फ़ंक्शन के साथ पूरी तरह से ठीक काम करेगा ।
अच्छा विचार:
चूँकि विवादास्पद फ़ंक्शन स्टेटफुल हैं, हमें प्रति घटक उदाहरण में एक डिबेंक्टेड फंक्शन बनाना होगा ।
ईएस 6 (वर्ग संपत्ति) : अनुशंसित
class SearchBox extends React.Component {
method = debounce(() => {
...
});
}
ईएस 6 (क्लास कंस्ट्रक्टर)
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
संदर्भ स्वयं वस्तु नहीं है (जो वास्तव में वास्तव में अभी तक मौजूद नहीं है, क्योंकि यह अभी बनाया जा रहा है)।
अच्छा विचार नहीं:
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 लॉग प्रविष्टि का निर्माण कर रहे हैं।
आपको प्रत्येक घटक उदाहरण के लिए एक डेबिट फ़ंक्शन बनाना होगा , और प्रत्येक घटक आवृत्ति द्वारा साझा की गई क्लास स्तर पर एक भी डेबिट फ़ंक्शन नहीं है।
रिएक्ट के इवेंट पूलिंग का ध्यान रखें
यह संबंधित है क्योंकि हम अक्सर डोम घटनाओं की चर्चा करना या गला घोंटना चाहते हैं।
प्रतिक्रिया में, SyntheticEvent
कॉलबैक में प्राप्त होने वाली ईवेंट ऑब्जेक्ट्स (यानी ) को पूल किया जाता है (यह अब प्रलेखित है )। इसका मतलब यह है कि ईवेंट कॉलबैक कहे जाने के बाद, आपके द्वारा प्राप्त SyntheticEvent को GC प्रेशर को कम करने के लिए खाली विशेषताओं के साथ पूल में वापस रखा जाएगा।
इसलिए यदि आप SyntheticEvent
मूल कॉलबैक के लिए अतुल्यकालिक रूप से गुणों का उपयोग करते हैं (जैसा कि यदि आप थ्रॉटल / डेब्यू करते हैं तो मामला हो सकता है), आपके द्वारा उपयोग किए जाने वाले गुण मिटाए जा सकते हैं। यदि आप चाहते हैं कि घटना को कभी भी पूल में वापस न रखा जाए, तो आप इसका उपयोग कर सकते हैंpersist()
विधि का ।
दृढ़ता के बिना (डिफ़ॉल्ट व्यवहार: पूलित घटना)
onClick = e => {
alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
setTimeout(() => {
alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
}, 0);
};
2 (async) प्रिंट करेगा hasNativeEvent=false
क्योंकि ईवेंट गुण साफ हो गए हैं।
हठ के साथ
onClick = e => {
e.persist();
alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
setTimeout(() => {
alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
}, 0);
};
दूसरा (async) प्रिंट करेगा hasNativeEvent=true
क्योंकि persist
आपको इवेंट को पूल में वापस रखने से बचने की अनुमति देता है।
आप इन 2 व्यवहारों का परीक्षण यहां कर सकते हैं: JsFiddle
एक थ्रॉटल / डिब्यूशन फ़ंक्शन के साथ उपयोग करने के उदाहरण के लिए जूलेन का जवाब पढ़ें persist()
।
debounce
। यहाँ, जबonChange={debounce(this.handleOnChange, 200)}/>
, यहdebounce function
हर बार आह्वान करेगा । लेकिन, वास्तव में, हमें जिस चीज की आवश्यकता होती है, वह फ़ंक्शन को इनवॉइस करता है, जो डेब्यू फ़ंक्शन को वापस करता है।