¿Cómo se realiza el rebote en React.js?
Quiero renunciar al handleOnChange.
Lo intenté debounce(this.handleOnChange, 200)
pero no funciona.
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
fuente
fuente
debounce
. aquí, cuandoonChange={debounce(this.handleOnChange, 200)}/>
invocarádebounce function
cada vez. pero, de hecho, lo que necesitamos es invocar la función que devolvió la función antirrebote.Respuestas:
2019: prueba ganchos + promesa de rebote
Esta es la versión más actualizada de cómo resolvería este problema. Yo usaría:
Este es un cableado inicial, pero está componiendo bloques primitivos por su cuenta, y puede hacer su propio gancho personalizado para que solo necesite hacer esto una vez.
Y luego puedes usar tu gancho:
Encontrará este ejemplo ejecutándose aquí y debe leer la documentación de react-async-hook para obtener más detalles.
2018: prueba la promesa de rebote
A menudo queremos eliminar las llamadas a la API para evitar inundar el backend con solicitudes inútiles.
En 2018, trabajar con devoluciones de llamada (Lodash / Underscore) se siente mal y propenso a errores. Es fácil encontrar problemas repetitivos y de simultaneidad debido a que las llamadas API se resuelven en un orden arbitrario.
He creado una pequeña biblioteca con React en mente para resolver tus dolores: awesome-debounce-promise .
Esto no debería ser más complicado que eso:
La función antirrebote asegura que:
this.setState({ result });
sucederá un solo por llamada APIEventualmente, puede agregar otro truco si su componente se desmonta:
Tenga en cuenta que los Observables (RxJS) también pueden ser una excelente opción para eliminar las entradas, pero es una abstracción más poderosa que puede ser más difícil de aprender / usar correctamente.
<2017: ¿todavía quieres usar la devolución de llamada?
La parte importante aquí es crear una sola función sin rebote (o estrangulada) por instancia de componente . No desea volver a crear la función antirrebote (o acelerador) cada vez, y no desea que ninguna de las instancias múltiples comparta la misma función antirrebote.
No estoy definiendo una función de eliminación de rebotes en esta respuesta, ya que no es realmente relevante, pero esta respuesta funcionará perfectamente bien con
_.debounce
subrayado o lodash, así como con cualquier función de eliminación de rebotes proporcionada por el usuario.BUENA IDEA:
Debido a que las funciones sin rebote tienen estado, tenemos que crear una función sin rebote por instancia de componente .
ES6 (propiedad de clase) : recomendado
ES6 (constructor de clase)
ES5
Ver JsFiddle : 3 instancias están produciendo 1 entrada de registro por instancia (eso hace 3 globalmente).
No es Buena idea:
No funcionará, porque durante la creación del objeto de descripción de clase,
this
no se crea el objeto en sí mismo.this.method
no devuelve lo que espera porque elthis
contexto no es el objeto en sí (que en realidad aún no existe, por cierto, ya que se está creando).No es Buena idea:
Esta vez está creando efectivamente una función sin rebote que llama a su
this.method
. ¡El problema es que lo está recreando en cadadebouncedMethod
llamada, por lo que la función antirrebote recién creada no sabe nada sobre las llamadas anteriores! Debe volver a usar la misma función de eliminación de rebote con el tiempo o la eliminación de rebote no sucederá.No es Buena idea:
Esto es un poco complicado aquí.
¡Todas las instancias montadas de la clase compartirán la misma función sin rebote, y la mayoría de las veces esto no es lo que quieres! Consulte JsFiddle : 3 instancias solo producen 1 entrada de registro a nivel mundial.
Debe crear una función sin rebote para cada instancia de componente , y no una sola función sin rebote a nivel de clase, compartida por cada instancia de componente.
Cuida la agrupación de eventos de React
Esto está relacionado porque a menudo queremos eliminar el rebote o limitar los eventos DOM.
En React, los objetos de evento (es decir,
SyntheticEvent
) que recibe en devoluciones de llamada se agrupan (esto ahora está documentado ). Esto significa que después de que se haya llamado la devolución de llamada del evento, el SyntheticEvent que reciba se volverá a colocar en el grupo con atributos vacíos para reducir la presión del GC.Por lo tanto, si accede a las
SyntheticEvent
propiedades de forma asincrónica a la devolución de llamada original (como puede ser el caso si acelera / elimina el rebote), las propiedades a las que accede pueden borrarse. Si desea que el evento nunca se vuelva a colocar en el grupo, puede usar elpersist()
método.Sin persistencia (comportamiento predeterminado: evento agrupado)
El segundo (asíncrono) se imprimirá
hasNativeEvent=false
porque las propiedades del evento se han limpiado.Con persistir
El segundo (asíncrono) se imprimirá
hasNativeEvent=true
porque lepersist
permite evitar volver a colocar el evento en el grupo.Puede probar estos 2 comportamientos aquí: JsFiddle
Lea la respuesta de Julen para ver un ejemplo de uso
persist()
con una función de aceleración / antirrebote.fuente
handleOnChange = debounce((e) => { /* onChange handler code here */ }, timeout)
en el nivel superior de su clase. Todavía está configurando efectivamente un miembro de instancia, pero se parece un poco más a una definición de método normal. No hay necesidad de unconstructor
si aún no tiene uno definido. Supongo que es principalmente una preferencia de estilo.componentWillUnmount
:this.method.cancel()
- de lo contrario, es posible que desee establecer State en un componente desmontado.Componentes no controlados
Puedes usar el
event.persist()
método .Sigue un ejemplo usando guiones bajos
_.debounce()
:Editar: Ver este JSFiddle
Componentes controlados
Actualización: el ejemplo anterior muestra un componente no controlado . Uso elementos controlados todo el tiempo, así que aquí hay otro ejemplo de lo anterior, pero sin usar el
event.persist()
"truco".Un JSFiddle también está disponible . Ejemplo sin guión bajo
Editar: ejemplos actualizados y JSFiddles to React 0.12
Editar: ejemplos actualizados para abordar el problema planteado por Sebastien Lorber
Editar: actualizado con jsfiddle que no usa guión bajo y usa JavaScript antirrebote.
fuente
<input value={this.props.someprop}...
, no se representará correctamente, ya que la actualización al presionar la tecla no vuelve al componente hasta después del rechazo. Está bien omitirvalue=
si está contento de que esto no se gestione, pero si desea rellenar previamente el valor y / o vincularlo en otro lugar, obviamente esto no funciona.persist
especialmente cuando puede haber muchos eventos, como enmousemove
. He visto que el código deja de responder de esa manera. Es mucho más eficiente extraer los datos necesarios del evento nativo en la llamada del evento, y luego llamar a la función sin rebote / estrangulada solo con los datos, NO el evento en sí. No es necesario que persista el evento de esa manera2019: use el gancho de reacción 'useCallback'
Después de probar muchos enfoques diferentes, descubrí que usar
useCallback
es el más simple y más eficiente para resolver el problema de llamadas múltiples de usardebounce
dentro de unonChange
evento.Según la documentación de la API de Hooks ,
Pasar una matriz vacía como dependencia asegura que la devolución de llamada se llame solo una vez. Aquí hay una implementación simple:
¡Espero que esto ayude!
fuente
debounce()
No considera que laonChange()
devolución de llamada sea el mismo método de devolución de llamada?const testFunc2 = useCallback(debounce((text) => console.log('testFunc2() has ran:', text), 1000) , []);
dentro del cuerpo del componente de función o React emite un mensaje de error sobre el uso del gancho fuera de él. Luego, en elonChange
controlador de eventos:<input type='text' name='name' className='th-input-container__input' onChange={evt => {testFunc2(evt.target.value);}}
.Encontré esta publicación de Justin Tulk muy útil. Después de un par de intentos, en lo que uno percibiría como la forma más oficial con react / redux, muestra que falla debido a la agrupación de eventos sintéticos de React . Su solución luego usa un estado interno para rastrear el valor cambiado / ingresado en la entrada, con una devolución de llamada justo después de lo
setState
cual llama a una acción redux acelerada / sin rebote que muestra algunos resultados en tiempo real.fuente
Si todo lo que necesita del objeto de evento es obtener el elemento de entrada DOM, la solución es mucho más simple: solo use
ref
. Tenga en cuenta que esto requiere subrayado :fuente
Después de luchar con las entradas de texto por un tiempo y no encontrar una solución perfecta por mi cuenta, encontré esto en npm: react-debounce-input .
Aquí hay un ejemplo simple:
El componente DebounceInput acepta todos los accesorios que puede asignar a un elemento de entrada normal. Pruébalo en codepen
Espero que también ayude a alguien más y les ahorre algo de tiempo.
fuente
Con
debounce
usted, debe mantener el evento sintético originalevent.persist()
. Aquí está el ejemplo de trabajo probado conReact 16+
.Con el componente funcional, puede hacer esto:
Referencias - - https://gist.github.com/elijahmanor/08fc6c8468c994c844213e4a4344a709 - https://blog.revathskumar.com/2016/02/reactjs-using-debounce-in-react-components.html
fuente
Si está utilizando redux, puede hacerlo de una manera muy elegante con middleware. Puede definir un
Debounce
middleware como:A continuación, puede agregar eliminación de rebotes a los creadores de acciones, como:
En realidad, ya hay middleware que puede usar npm para hacer esto por usted.
fuente
applyMiddleware(...)
cadena si tenemos muchosUsando ES6 CLASS y React 15.xx & lodash.debounce Estoy usando las referencias de React aquí ya que el evento pierde el enlace interno.
fuente
Mucha información buena aquí ya, pero para ser sucinto. Esto funciona para mi ...
fuente
_debounce
contenedor, funciona. ¡Aunque me encanta esta idea!Puede usar el método Lodash debounce https://lodash.com/docs/4.17.5#debounce . Es simple y efectivo.
También puede cancelar el método antirrebote utilizando el siguiente método.
Espero que te ayude. ¡¡Salud!!
fuente
FYI
Aquí hay otra implementación de PoC:
Espero que ayude :)
fuente
Hay un
use-debounce
paquete que puede usar con los ganchos ReactJS.Del paquete README:
Como puede ver en el ejemplo anterior, está configurado para actualizar la variable
value
solo una vez por segundo (1000 milisegundos).fuente
Solo otra variante con reacción reciente y lodash.
fuente
Una solución agradable y limpia, que no requiere dependencias externas:
Debouncing con React Hooks
Utiliza un personalizado más los ganchos useEffect React y el método
setTimeout
/clearTimeout
.fuente
¿Has probado?
fuente
En lugar de envolver el handleOnChange en un debounce (), ¿por qué no envolver la llamada ajax dentro de la función de devolución de llamada dentro del debounce, sin destruir el objeto de evento? Entonces algo como esto:
fuente
Aquí hay un ejemplo que se me ocurrió que envuelve otra clase con un rebotador. Esto se presta muy bien para convertirse en una función de decorador / orden superior:
fuente
Ahora hay otra solución para React y React Native a fines de 2019 :
componente react-debounce
Es un componente, fácil de usar, pequeño y compatible con Widley
Ejemplo:
* Soy el creador de este componente
fuente
Estaba buscando una solución para el mismo problema y encontré este hilo así como algunos otros, pero tenían el mismo problema: si está tratando de hacer una
handleOnChange
función y necesita el valor de un objetivo de evento, obtendrácannot read property value of null
o algo tal error En mi caso, también necesitaba preservar el contextothis
dentro de la función eliminada ya que estoy ejecutando una acción fluible. Aquí está mi solución, funciona bien para mi caso de uso, así que la dejo aquí en caso de que alguien se encuentre con este hilo:fuente
para
throttle
odebounce
la mejor manera es crear un creador de funciones para que pueda usarlo en cualquier lugar, por ejemplo:y en tu
render
método puedes hacer:la
updateUserProfileField
método creará una función separada cada vez que lo llame.Tenga en cuenta que no intente devolver el controlador directamente, por ejemplo, esto no funcionará:
la razón por la cual esto no funcionará porque generará una nueva función de aceleración cada vez que se llame el evento en lugar de usar la misma función de aceleración, por lo que básicamente la aceleración será inútil;)
Además, si usa
debounce
othrottle
no necesitasetTimeout
oclearTimeout
, esta es realmente la razón por la que los usamos: Pfuente
Aquí hay un fragmento que utiliza el enfoque de @ Abra envuelto en un componente de función (usamos fabric para la interfaz de usuario, solo reemplácelo con un botón simple)
fuente
Mi solución está basada en ganchos (escrita en mecanografiado).
Tengo 2 ganchos principales
useDebouncedValue
yuseDebouncedCallback
Primero -
useDebouncedValue
Digamos que tenemos un cuadro de búsqueda, pero queremos pedirle al servidor los resultados de búsqueda después de que el usuario haya dejado de escribir durante 0,5 segundos.
Implementación
Segundo
useDebouncedCallback
Simplemente crea una función 'sin rebote' en el alcance de su componente.
Digamos que tenemos un componente con un botón que mostrará la alerta 500ms después de que dejaste de hacer clic.
Implementación (tenga en cuenta que estoy usando lodash / debounce como ayudante)
fuente
Aquí hay un ejemplo de TypeScript que funciona para aquellos que usan TS y desean eliminar las
async
funciones.fuente
un poco tarde aquí, pero esto debería ayudar. crear esta clase (está escrito en mecanografiado pero es fácil convertirlo a javascript)
y para usar
fuente
puedes usar tlence tlence
fuente
La solución de Julen es un poco difícil de leer, aquí hay un código más claro y directo para cualquiera que lo haya tropezado según el título y no los pequeños detalles de la pregunta.
tl; versión dr : cuando actualizaría a los observadores, envíe una llamada a un método de programación y eso a su vez notificará a los observadores (o realizará ajax, etc.)
Complete jsfiddle con el componente de ejemplo jsfiddle
fuente
Evite usar
event.persist()
: desea dejar que React recicle el evento sintético. Creo que la forma más limpia de usar clases o ganchos es dividir la devolución de llamada en dos partes:Clases
Las funciones
Tenga en cuenta que si su
handleMouseOver
función usa el estado desde el componente, debe usarlo enuseMemo
lugar deuseRef
y pasarlos como dependencias; de lo contrario, trabajará con datos obsoletos (no se aplica a las clases, por supuesto).fuente
Ampliar uso Gancho de estado
Usa gancho
https://trippingoncode.com/react-debounce-hook/
fuente
Encontré este problema hoy. Lo resolvió usando
setTimeout
yclearTimeout
.Le daré un ejemplo que podría adaptar:
También puede refactorizarlo en su propio componente de función:
Y úsalo como:
fuente