Tengo un formulario con entrada de nombre de usuario y estoy tratando de verificar si el nombre de usuario está en uso o no en una función de rebote. El problema que tengo es que mi rebote no parece funcionar, ya que cuando escribo "usuario" mi consola se ve como
u us use userAquí está mi función de rebote
export function debounce(func, wait, immediate) { var timeout; return () => { var context = this, args = arguments; var later = () => { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; };Y así es como lo llamo en mi componente React
import React, { useEffect } from 'react' // verify username useEffect(() => { if(state.username !== "") { verify(); } }, [state.username]) const verify = debounce(() => { console.log(state.username) }, 1000);¿La función de rebote parece ser correcta? ¿Hay algún problema con la forma en que lo estoy llamando en reaccionar?
Cada vez que su componente se vuelve a renderizar, se crea una nueva función de verify de rebote, lo que significa que dentro useEffect en realidad está llamando a diferentes funciones que anulan el propósito de la eliminación de rebote.
Es como si estuvieras haciendo algo como esto:
const debounced1 = debounce(() => { console.log(state.username) }, 1000); debounced1(); const debounced2 = debounce(() => { console.log(state.username) }, 1000); debounced2(); const debounced3 = debounce(() => { console.log(state.username) }, 1000); debounced3();a diferencia de lo que realmente quieres:
const debounced = debounce(() => { console.log(state.username) }, 1000); debounced(); debounced(); debounced(); Una forma de resolver esto es usar useCallback , que siempre devolverá la misma devolución de llamada (cuando pasa una matriz vacía como segundo argumento), también, pasaría el nombre de username a esta función en lugar de acceder al estado interno (de lo contrario, estar accediendo a un estado obsoleto):
import { useCallback } from "react"; const App => () { const [username, setUsername] = useState(""); useEffect(() => { if (username !== "") { verify(username); } }, [username]); const verify = useCallback( debounce(name => { console.log(name); }, 200), [] ); return <input onChange={e => setUsername(e.target.value)} />; }También necesita actualizar ligeramente su función de rebote, ya que no pasa los argumentos correctamente a la función de rebote.
function debounce(func, wait, immediate) { var timeout; return (...args) => { <--- needs to use this `args` instead of the ones belonging to the enclosing scope var context = this; ...Sugiero algunos cambios.
1) Cada vez que realiza un cambio de estado, activa un renderizado. Cada render tiene sus propios accesorios y efectos. Entonces, su useEffect está generando una nueva función de rebote cada vez que actualiza el nombre de usuario. Este es un buen caso para useCallback ganchos para mantener la instancia de la función igual entre los renderizados, o posiblemente useRef tal vez - me quedo con useCallback yo mismo.
2) Separaría los controladores individuales en lugar de usar useEffect para activar su rebote: termina teniendo una larga lista de dependencias a medida que su componente crece y no es el mejor lugar para esto.
3) Su función de rebote no se ocupa de los parámetros. (Reemplacé con lodash.debouce, pero puede depurar su implementación)
4) Creo que aún desea actualizar el estado al presionar una tecla, pero solo ejecute su función denunciada cada x segundos
Ejemplo:
import React, { useState, useCallback } from "react"; import "./styles.css"; import debounce from "lodash.debounce"; export default function App() { const [username, setUsername] = useState(''); const verify = useCallback( debounce(username => { console.log(`processing ${username}`); }, 1000), [] ); const handleUsernameChange = event => { setUsername(event.target.value); verify(event.target.value); }; return ( <div className="App"> <h1>Debounce</h1> <input type="text" value={username} onChange={handleUsernameChange} /> </div> ); }Recomiendo encarecidamente leer esta gran publicación sobre useEffect y hooks.
Aquí hay una mejor manera de resolver esto;)
export function useLazyEffect(effect: EffectCallback, deps: DependencyList = [], wait = 300) { const cleanUp = useRef<void | (() => void)>(); const effectRef = useRef<EffectCallback>(); const updatedEffect = useCallback(effect, deps); effectRef.current = updatedEffect; const lazyEffect = useCallback( _.debounce(() => { cleanUp.current = effectRef.current?.(); }, wait), [], ); useEffect(lazyEffect, deps); useEffect(() => { return () => { cleanUp.current instanceof Function ? cleanUp.current() : undefined; }; }, []); }