Comment effectuez-vous un anti-rebond dans React.js?
Je veux faire rebondir le handleOnChange.
J'ai essayé avec debounce(this.handleOnChange, 200)
mais ça ne marche pas.
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
}
});
javascript
reactjs
Chetan Ankola
la source
la source
debounce
. ici, quandonChange={debounce(this.handleOnChange, 200)}/>
, il invoquera àdebounce function
chaque fois. mais, en fait, ce dont nous avons besoin est d'invoquer la fonction que la fonction anti-rebond a renvoyée.Réponses:
2019: essayez les hooks + promets debouncing
Ceci est la version la plus récente de la façon dont je résoudrais ce problème. J'utiliserais:
Il s'agit d'un câblage initial, mais vous composez vous-même des blocs primitifs et vous pouvez créer votre propre crochet personnalisé de sorte que vous n'ayez besoin de le faire qu'une seule fois.
Et puis vous pouvez utiliser votre crochet:
Vous trouverez cet exemple en cours d'exécution ici et vous devriez lire la documentation react-async-hook pour plus de détails.
2018: essayez de promettre un rebond
Nous voulons souvent rebondir les appels d'API pour éviter d'inonder le backend de requêtes inutiles.
En 2018, travailler avec des rappels (Lodash / Underscore) me fait du mal et est sujet à erreur. Il est facile de rencontrer des problèmes de passe-partout et de concurrence en raison de la résolution d'appels d'API dans un ordre arbitraire.
J'ai créé une petite bibliothèque avec React en tête pour résoudre vos douleurs: awesome-debounce-promise .
Cela ne devrait pas être plus compliqué que cela:
La fonction anti-rebond garantit que:
this.setState({ result });
se produira par appel d'APIFinalement, vous pouvez ajouter une autre astuce si votre composant se démonte:
Notez que Observables (RxJS) peut également être un excellent choix pour les entrées anti-rebond, mais c'est une abstraction plus puissante qui peut être plus difficile à apprendre / à utiliser correctement.
<2017: vous voulez toujours utiliser la fonction anti-rebond?
La partie importante ici est de créer une seule fonction anti-rebond (ou limitée) par instance de composant . Vous ne voulez pas recréer la fonction anti-rebond (ou limitation) à chaque fois, et vous ne voulez pas que plusieurs instances partagent la même fonction anti-rebond.
Je ne définis pas de fonction anti-rebond dans cette réponse car elle n'est pas vraiment pertinente, mais cette réponse fonctionnera parfaitement avec le
_.debounce
soulignement ou le lodash, ainsi qu'avec toute fonction anti-rebond fournie par l'utilisateur.BONNE IDÉE:
Parce que les fonctions anti-rebond sont avec état, nous devons créer une fonction anti-rebond par instance de composant .
ES6 (propriété de classe) : recommandé
ES6 (constructeur de classe)
ES5
Voir JsFiddle : 3 instances produisent 1 entrée de journal par instance (ce qui en fait 3 globalement).
Pas une bonne idée:
Cela ne fonctionnera pas, car lors de la création de l'objet description de classe, ce
this
n'est pas l'objet créé lui-même.this.method
ne renvoie pas ce que vous attendez car lethis
contexte n'est pas l'objet lui-même (qui en réalité n'existe pas encore vraiment BTW car il vient d'être créé).Pas une bonne idée:
Cette fois, vous créez effectivement une fonction anti-rebond qui appelle votre
this.method
. Le problème est que vous le recréez à chaquedebouncedMethod
appel, donc la fonction anti-rebond nouvellement créée ne sait rien des anciens appels! Vous devez réutiliser la même fonction anti-rebond au fil du temps, sinon le rebouncing ne se produira pas.Pas une bonne idée:
C'est un peu délicat ici.
Toutes les instances montées de la classe partageront la même fonction anti-rebond, et le plus souvent ce n'est pas ce que vous voulez!. Voir JsFiddle : 3 instances ne produisent qu'une seule entrée de journal dans le monde.
Vous devez créer une fonction anti-rebond pour chaque instance de composant , et non une seule fonction anti-rebond au niveau de la classe, partagée par chaque instance de composant.
Prenez soin de la mise en commun des événements de React
Ceci est lié au fait que nous voulons souvent rebondir ou limiter les événements DOM.
Dans React, les objets d'événement (c'est-à-dire
SyntheticEvent
) que vous recevez dans les rappels sont regroupés (cela est maintenant documenté ). Cela signifie qu'après l'appel du rappel d'événement, le SyntheticEvent que vous recevez sera remis dans le pool avec des attributs vides pour réduire la pression du GC.Ainsi, si vous accédez aux
SyntheticEvent
propriétés de manière asynchrone par rapport au rappel d'origine (comme cela peut être le cas si vous accélérez / rebondissez), les propriétés auxquelles vous accédez peuvent être effacées. Si vous souhaitez que l'événement ne soit jamais remis dans le pool, vous pouvez utiliser lapersist()
méthode.Sans persister (comportement par défaut: événement groupé)
Le 2ème (async) s'imprimera
hasNativeEvent=false
car les propriétés de l'événement ont été nettoyées.Avec persister
Le 2ème (async) s'imprimera
hasNativeEvent=true
carpersist
vous permet d'éviter de remettre l'événement dans le pool.Vous pouvez tester ces 2 comportements ici: JsFiddle
Lisez la réponse de Julen pour un exemple d'utilisation
persist()
avec une fonction de limitation / anti-rebond.la source
handleOnChange = debounce((e) => { /* onChange handler code here */ }, timeout)
au niveau supérieur de votre classe. Vous définissez toujours efficacement un membre d'instance, mais cela ressemble un peu plus à une définition de méthode normale. Pas besoin d'unconstructor
si vous n'en avez pas déjà défini un. Je suppose que c'est surtout une préférence de style.componentWillUnmount
:this.method.cancel()
- sinon il peut vouloir définir setState sur un composant non monté.Composants non contrôlés
Vous pouvez utiliser la
event.persist()
méthode .Un exemple suit en utilisant les traits de soulignement
_.debounce()
:Edit: Voir ce JSFiddle
Composants contrôlés
Mise à jour: l'exemple ci-dessus montre un composant non contrôlé . J'utilise des éléments contrôlés tout le temps, voici donc un autre exemple de ce qui précède, mais sans utiliser la
event.persist()
"ruse".Un JSFiddle est également disponible . Exemple sans soulignement
Edit: exemples mis à jour et JSFiddles pour React 0.12
Edit: exemples mis à jour pour résoudre le problème soulevé par Sébastien Lorber
Edit: mis à jour avec jsfiddle qui n'utilise pas de soulignement et utilise un anti-rebond javascript simple.
la source
<input value={this.props.someprop}...
il ne s'affichera pas correctement car la mise à jour à la pression de la touche ne revient pas dans le composant avant le rebond. Il est bon d'omettre levalue=
si vous êtes content que cela ne soit pas géré, mais si vous souhaitez pré-remplir la valeur et / ou la lier ailleurs, cela ne fonctionne évidemment pas.persist
particulier lorsqu'il peut y avoir beaucoup d'événements, comme lemousemove
. J'ai vu le code devenir totalement insensible de cette façon. Il est beaucoup plus efficace d'extraire les données nécessaires de l'événement natif dans l'appel d'événement, puis d'appeler la fonction anti-rebond / limitée avec les données uniquement, PAS l'événement lui-même. Pas besoin de persister l'événement de cette façon2019: utilisez le crochet réactif 'useCallback'
Après avoir essayé de nombreuses approches différentes, j'ai trouvé que l'utilisation
useCallback
était la plus simple et la plus efficace pour résoudre le problème des appels multiples de l'utilisationdebounce
dans unonChange
événement.Selon la documentation de l'API Hooks ,
Le passage d'un tableau vide en tant que dépendance garantit que le rappel n'est appelé qu'une seule fois. Voici une implémentation simple:
J'espère que cela t'aides!
la source
debounce()
considère pas que leonChange()
rappel est la même méthode de rappel?const testFunc2 = useCallback(debounce((text) => console.log('testFunc2() has ran:', text), 1000) , []);
à l'intérieur du corps du composant de fonction ou React génère un message d'erreur sur l'utilisation du crochet en dehors de celui-ci. Ensuite , dans leonChange
gestionnaire d'événements:<input type='text' name='name' className='th-input-container__input' onChange={evt => {testFunc2(evt.target.value);}}
.J'ai trouvé cet article de Justin Tulk très utile. Après quelques tentatives, dans ce que l'on percevrait comme la manière la plus officielle avec react / redux, cela montre qu'il échoue en raison de la mise en commun synthétique des événements de React . Sa solution utilise ensuite un état interne pour suivre la valeur modifiée / entrée dans l'entrée, avec un rappel juste après
setState
lequel appelle une action redux limitée / rebondie qui affiche des résultats en temps réel.la source
Si tout ce dont vous avez besoin de l'objet événement est d'obtenir l'élément d'entrée DOM, la solution est beaucoup plus simple - il suffit de l'utiliser
ref
. Notez que cela nécessite Underscore :la source
Après avoir lutté avec les entrées de texte pendant un certain temps et n'ayant pas trouvé de solution parfaite par moi-même, j'ai trouvé cela sur npm: react-debounce-input .
Voici un exemple simple:
Le composant DebounceInput accepte tous les accessoires que vous pouvez affecter à un élément d'entrée normal. Essayez-le sur codepen
J'espère que cela aide aussi quelqu'un d'autre et lui fait gagner du temps.
la source
Avec,
debounce
vous devez conserver l'événement synthétique d'origine avecevent.persist()
. Voici un exemple de travail testé avecReact 16+
.Avec le composant fonctionnel, vous pouvez le faire -
Références - - https://gist.github.com/elijahmanor/08fc6c8468c994c844213e4a4344a709 - https://blog.revathskumar.com/2016/02/reactjs-using-debounce-in-react-components.html
la source
Si vous utilisez redux, vous pouvez le faire de manière très élégante avec un middleware. Vous pouvez définir un
Debounce
middleware comme:Vous pouvez ensuite ajouter un anti-rebond aux créateurs d'actions, tels que:
Il existe en fait déjà un middleware que vous pouvez supprimer de npm pour le faire pour vous.
la source
applyMiddleware(...)
chaîne si nous en avons plusieursEn utilisant ES6 CLASS et React 15.xx & lodash.debounce Im en utilisant les références de React ici car les pertes d'événement le lient en interne.
la source
Beaucoup de bonnes informations ici déjà, mais pour être succinct. Cela fonctionne pour moi ...
la source
_debounce
emballage, cela fonctionne. J'aime bien cette idée!Vous pouvez utiliser la méthode https://lodash.com/docs/4.17.5#debounce de Lodash debounce . C'est simple et efficace.
Vous pouvez également annuler la méthode anti-rebond en utilisant la méthode ci-dessous.
J'espère que cela vous aide. À votre santé!!
la source
FYI
Voici une autre implémentation PoC:
J'espère que ça aide :)
la source
Il existe un
use-debounce
package que vous pouvez utiliser avec les crochets ReactJS.À partir du fichier README du package:
Comme vous pouvez le voir dans l'exemple ci-dessus, il est configuré pour mettre à jour la variable
value
une seule fois par seconde (1000 millisecondes).la source
Juste une autre variante avec une réaction et un lodash récents.
la source
Une solution agréable et propre, qui ne nécessite aucune dépendance externe:
Anti-rebond avec React Hooks
Il utilise un personnalisé plus les hooks useEffect React et la méthode
setTimeout
/clearTimeout
.la source
As-tu essayé?
la source
Au lieu d'envelopper le handleOnChange dans un debounce (), pourquoi ne pas envelopper l'appel ajax dans la fonction de rappel à l'intérieur du debounce, ne détruisant ainsi pas l'objet événement. Donc quelque chose comme ça:
la source
Voici un exemple que j'ai trouvé qui encapsule une autre classe avec un debouncer. Cela se prête bien à être transformé en fonction de décorateur / d'ordre supérieur:
la source
Il existe désormais une autre solution pour React et React Native fin 2019 :
composant react-debounce
C'est un composant, facile à utiliser, minuscule et pris en charge par widley
Exemple:
* Je suis le créateur de ce composant
la source
Je cherchais une solution au même problème et suis tombé sur ce fil ainsi que sur d'autres, mais ils avaient le même problème: si vous essayez de faire une
handleOnChange
fonction et que vous avez besoin de la valeur d'une cible d'événement, vous obtiendrezcannot read property value of null
ou certains une telle erreur. Dans mon cas, j'avais également besoin de préserver le contexte de l'this
intérieur de la fonction anti-rebond puisque j'exécute une action fluxable. Voici ma solution, cela fonctionne bien pour mon cas d'utilisation, donc je la laisse ici au cas où quelqu'un rencontrerait ce fil:la source
pour
throttle
oudebounce
la meilleure façon est de créer un créateur de fonction afin que vous puissiez l' utiliser tout où, par exemple:et dans votre
render
méthode, vous pouvez faire:la
updateUserProfileField
méthode créera une fonction séparée à chaque appel.Note ne pas essayer de retourner le gestionnaire directement par exemple cela ne fonctionnera pas:
la raison pour laquelle cela ne fonctionnera pas car cela générera une nouvelle fonction de limitation chaque fois que l'événement sera appelé au lieu d'utiliser la même fonction de limitation, de sorte que la limitation sera inutile;)
Aussi , si vous utilisez
debounce
outhrottle
vous n'avez pas besoinsetTimeout
ouclearTimeout
, c'est en fait la raison pour laquelle nous les utilisons: Pla source
Voici un extrait utilisant l'approche d'Abra @ enveloppé dans un composant de fonction (nous utilisons le tissu pour l'interface utilisateur, il suffit de le remplacer par un simple bouton)
la source
Ma solution est basée sur les hooks (écrite en Typescript).
J'ai 2 crochets principaux
useDebouncedValue
etuseDebouncedCallback
Première -
useDebouncedValue
Disons que nous avons une boîte de recherche, mais nous voulons demander au serveur les résultats de la recherche après que l'utilisateur a arrêté de taper pendant 0,5 s
la mise en oeuvre
Seconde
useDebouncedCallback
Il crée simplement une fonction «anti-rebond» dans la portée de votre composant.
Disons que nous avons un composant avec un bouton qui affichera une alerte 500 ms après avoir cessé de cliquer dessus.
Implémentation (notez que j'utilise lodash / debounce comme aide)
la source
Voici un exemple TypeScript fonctionnel pour ceux qui utilisent TS et qui souhaitent rebondir sur les
async
fonctions.la source
un peu tard ici mais cela devrait aider. créer cette classe (son écrit en tapuscrit mais il est facile de le convertir en javascript)
et utiliser
la source
vous pouvez utiliser tlence tlence
la source
La solution de Julen est un peu difficile à lire, voici un code de réaction plus clair et précis pour quiconque l'a trébuché en fonction du titre et non des petits détails de la question.
tl; dr version : lorsque vous mettriez à jour les observateurs, envoyez un appel à une méthode de planification à la place et cela à son tour avertira les observateurs (ou effectuera ajax, etc.)
Jsfiddle complet avec exemple de composant jsfiddle
la source
Évitez d'utiliser
event.persist()
- vous voulez laisser React recycler l'événement synthétique. Je pense que le moyen le plus propre, que vous utilisiez des classes ou des hooks, est de diviser le rappel en deux parties:Des classes
Les fonctions
Notez que si votre
handleMouseOver
fonction utilise l' état de l' intérieur du composant, vous devez utiliser auuseMemo
lieu deuseRef
et transmettre les dépendances que sinon vous allez travailler avec des données périmées (ne concerne pas les classes de cours).la source
Étendre le crochet useState
Utiliser un crochet
https://trippingoncode.com/react-debounce-hook/
la source
J'ai rencontré ce problème aujourd'hui. Résolu en utilisant
setTimeout
etclearTimeout
.Je vais donner un exemple que vous pourriez adapter:
Vous pouvez également le refactoriser dans son propre composant de fonction:
Et utilisez-le comme:
la source