2019: Kancaları dene + geri dönme sözü
Bu sorunu nasıl çözeceğimin en güncel sürümüdür. Kullanmak istiyorum:
Bu bazı ilk kablolamadır, ancak kendi başınıza ilkel bloklar oluşturuyorsunuz ve bunu sadece bir kez yapmanız için kendi özel kancanızı yapabilirsiniz.
// 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,
};
};
Ve sonra kancayı kullanabilirsiniz:
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>
);
};
Burada çalışan bu örneği bulacaksınız ve daha fazla bilgi için reaktif asenkron kanca belgelerini okumalısınız .
2018: Geri Dönme Sözünü Deneyin
Arka ucu işe yaramaz isteklerle doldurmamak için API çağrılarını sık sık iptal etmek istiyoruz.
2018'de geri aramalarla çalışmak (Lodash / Underscore) bana kötü ve hataya açık geliyor. Keyfi bir sırayla çözülen API çağrıları nedeniyle ortak plaka ve eşzamanlılık sorunları ile karşılaşmak kolaydır.
Acılarınızı çözmek için React düşünülerek küçük bir kütüphane oluşturdum: harika-debounce-söz .
Bu bundan daha karmaşık olmamalıdır:
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 });
};
}
Çıkarılan fonksiyon şunları sağlar:
- API çağrıları iptal edilecektir
- geri alınan işlev her zaman bir söz verir
- sadece son aramanın iade edilen sözü çözülecek
this.setState({ result });
API çağrısı başına bir tane olur
Sonunda, bileşeninizin bağlantısı kesilirse başka bir numara ekleyebilirsiniz:
componentWillUnmount() {
this.setState = () => {};
}
Not olduğunu Gözlenebilirler (RxJS) ayrıca girişler zıplamaölçer için ideal olabilir, ancak öğrenmek için zor olabilir daha güçlü bir soyutlama / doğru kullanın.
<2017: hala geri arama iptali özelliğini kullanmak istiyor musunuz?
Buradaki önemli kısım, bileşen örneği başına tek bir kaldırılmış (veya kısaltılmış) işlev oluşturmaktır . Her defasında debounce (veya gaz kelebeği) işlevini yeniden oluşturmak istemezsiniz ve birden çok örneğin aynı debounced işlevini paylaşmasını istemezsiniz.
Bu cevapta gerçekten ilgili olmadığı için bir geri dönme işlevi tanımlamıyorum, ancak bu cevap _.debounce
alt çizgi veya lodash'ın yanı sıra kullanıcı tarafından sağlanan herhangi bir geri dönme işleviyle mükemmel şekilde çalışacaktır.
İYİ BİR FİKİR:
Ayrılmış işlevler durumsal olduğundan, bileşen örneği başına bir adet kaldırılmış işlev oluşturmanız gerekir .
ES6 (sınıf özelliği) : önerilir
class SearchBox extends React.Component {
method = debounce(() => {
...
});
}
ES6 (sınıf yapıcı)
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);
},
});
Bkz. JsFiddle : 3 örnek, örnek başına 1 günlük girişi üretmektedir (bu, global olarak 3'tür).
İyi bir fikir değil:
var SearchBox = React.createClass({
method: function() {...},
debouncedMethod: debounce(this.method, 100);
});
Sınıf açıklama nesnesi oluşturma sırasında, this
kendisi oluşturulan nesne değildir çünkü çalışmaz . this.method
beklediğiniz şeyi döndürmez, çünkü this
bağlam nesnenin kendisi değildir (gerçekte henüz yaratılmakta olan BTW henüz mevcut değildir).
İyi bir fikir değil:
var SearchBox = React.createClass({
method: function() {...},
debouncedMethod: function() {
var debounced = debounce(this.method,100);
debounced();
},
});
Bu kez etkin bir şekilde sizin çağrı çağıran bir işlevi yaratırsınız this.method
. Sorun şu ki, her debouncedMethod
çağrıda yeniden oluşturuyorsunuz, bu yüzden yeni oluşturulan geri dönme fonksiyonu eski çağrılar hakkında hiçbir şey bilmiyor! Zamanla aynı geri dönen işlevi yeniden kullanmalısınız, aksi takdirde geri alma gerçekleşmez.
İyi bir fikir değil:
var SearchBox = React.createClass({
debouncedMethod: debounce(function () {...},100),
});
Bu biraz zor.
Sınıfın tüm monte edilmiş örnekleri aynı ayrılmış işlevi paylaşacaktır ve çoğu zaman bu istediğiniz şey değildir !. Bkz. JsFiddle : Dünya çapında 3 örnek yalnızca 1 günlük girişi üretiyor .
Her bileşen örneği tarafından paylaşılan, sınıf düzeyinde tek bir debounced işlevi değil, her bileşen örneği için bir debounced işlevi oluşturmanız gerekir .
React'in etkinlik havuzuna dikkat edin
Bu, DOM etkinliklerini sık sık kaldırmak veya kısıtlamak istediğimiz için ilgilidir.
React'te, SyntheticEvent
geri çağrılarda aldığınız olay nesneleri (ör. ) Toplanır (bu artık belgelenmiştir ). Bu, olay geri çağrısı çağrıldıktan sonra, aldığınız SyntheticEvent'in GC basıncını azaltmak için boş niteliklerle havuza geri konulacağı anlamına gelir.
Bu nedenle, SyntheticEvent
özelliklere orijinal geri aramayla eşzamansız olarak erişirseniz (kısma / yeniden başlatma durumunda olduğu gibi), eriştiğiniz özellikler silinebilir. Etkinliğin bir daha asla havuza konmamasını istiyorsanız, persist()
yöntemi kullanabilirsiniz .
Kalıcı olmadan (varsayılan davranış: havuzlanmış olay)
onClick = e => {
alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
setTimeout(() => {
alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
}, 0);
};
hasNativeEvent=false
Etkinlik özellikleri temizlendiği için 2. (zaman uyumsuz) yazdırılır .
Kalıcı olarak
onClick = e => {
e.persist();
alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
setTimeout(() => {
alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
}, 0);
};
2. (zaman uyumsuz) yazdırılacaktır, hasNativeEvent=true
çünkü persist
etkinliği tekrar havuza koymaktan kaçınmanıza olanak tanır.
Bu 2 davranışı burada test edebilirsiniz: JsFiddle
Gaz kelebeği / debounce işleviyle kullanım örneği için Julen'in cevabını okuyun persist()
.
debounce
. burada, her seferindeonChange={debounce(this.handleOnChange, 200)}/>
çağıracakdebounce function
. ama aslında, ihtiyacımız olan şey, geri alma işlevinin döndürdüğü işlevi çağırmaktır.