Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

121
Vistas
Eliminación de rebotes y tiempo de espera en React

Aquí tengo un campo de entrada que en cada tipo, envía una acción de reducción. He puesto un useDebounce para que no sea muy pesado. El problema es que dice que Hooks can only be called inside of the body of a function component. ¿Cuál es la forma correcta de hacerlo?

useTimeout

 import { useCallback, useEffect, useRef } from "react"; export default function useTimeout(callback, delay) { const callbackRef = useRef(callback); const timeoutRef = useRef(); useEffect(() => { callbackRef.current = callback; }, [callback]); const set = useCallback(() => { timeoutRef.current = setTimeout(() => callbackRef.current(), delay); }, [delay]); const clear = useCallback(() => { timeoutRef.current && clearTimeout(timeoutRef.current); }, []); useEffect(() => { set(); return clear; }, [delay, set, clear]); const reset = useCallback(() => { clear(); set(); }, [clear, set]); return { reset, clear }; }

usarDebounce

 import { useEffect } from "react"; import useTimeout from "./useTimeout"; export default function useDebounce(callback, delay, dependencies) { const { reset, clear } = useTimeout(callback, delay); useEffect(reset, [...dependencies, reset]); useEffect(clear, []); }

Componente de formulario

 import React from "react"; import TextField from "@mui/material/TextField"; import useDebounce from "../hooks/useDebounce"; export default function ProductInputs(props) { const { handleChangeProductName = () => {} } = props; return ( <TextField fullWidth label="Name" variant="outlined" size="small" name="productName" value={formik.values.productName} helperText={formik.touched.productName ? formik.errors.productName : ""} error={formik.touched.productName && Boolean(formik.errors.productName)} onChange={(e) => { formik.setFieldValue("productName", e.target.value); useDebounce(() => handleChangeProductName(e.target.value), 1000, [ e.target.value, ]); }} /> ); }
about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

No creo que los ganchos React sean adecuados para una función de aceleración o rebote. Por lo que entiendo de su pregunta, efectivamente desea eliminar el rebote de la función handleChangeProductName .

Aquí hay una función simple de orden superior que puede usar para decorar una función de devolución de llamada para eliminar el rebote. Si la función devuelta se vuelve a invocar antes de que expire el tiempo de espera, el tiempo de espera se borra y se vuelve a crear. Solo cuando expira el tiempo de espera, se invoca la función decorada y se pasan los argumentos.

 const debounce = (fn, delay) => { let timerId; return (...args) => { clearTimeout(timerId); timerId = setTimeout(() => fn(...args), delay); } };

Ejemplo de uso:

 export default function ProductInputs({ handleChangeProductName }) { const debouncedHandler = useCallback(debounce(handleChangeProductName, 200), []); return ( <TextField fullWidth label="Name" variant="outlined" size="small" name="productName" value={formik.values.productName} helperText={formik.touched.productName ? formik.errors.productName : ""} error={formik.touched.productName && Boolean(formik.errors.productName)} onChange={(e) => { formik.setFieldValue("productName", e.target.value); debouncedHandler(e.target.value); }} /> ); }

Si es posible, el componente principal que pasa la devolución de llamada handleChangeProductName como accesorio probablemente debería manejar la creación de un controlador memorizado y sin rebote, pero lo anterior también debería funcionar.

about 4 years ago · Juan Pablo Isaza Denunciar

0

Echando un vistazo a su implementación de useDebounce , y no parece muy útil como gancho. Parece haberse hecho cargo del trabajo de llamar a su función y no devuelve nada, pero la mayor parte de su implementación se realiza en useTimeout , que tampoco hace mucho...

En mi opinión, useDebounce debería devolver una versión "recuperada" de callback de llamada

Aquí está mi opinión sobre useDebounce :

 export default function useDebounce(callback, delay) { const [debounceReady, setDebounceReady] = useState(true); const debouncedCallback = useCallback((...args) => { if (debounceReady) { callback(...args); setDebounceReady(false); } }, [debounceReady, callback]); useEffect(() => { if (debounceReady) { return undefined; } const interval = setTimeout(() => setDebounceReady(true), delay); return () => clearTimeout(interval); }, [debounceReady, delay]); return debouncedCallback; }

El uso se verá algo como:

 import React from "react"; import TextField from "@mui/material/TextField"; import useDebounce from "../hooks/useDebounce"; export default function ProductInputs(props) { const handleChangeProductName = useCallback((value) => { if (props.handleChangeProductName) { props.handleChangeProductName(value); } else { // do something else... }; }, [props.handleChangeProductName]); const debouncedHandleChangeProductName = useDebounce(handleChangeProductName, 1000); return ( <TextField fullWidth label="Name" variant="outlined" size="small" name="productName" value={formik.values.productName} helperText={formik.touched.productName ? formik.errors.productName : ""} error={formik.touched.productName && Boolean(formik.errors.productName)} onChange={(e) => { formik.setFieldValue("productName", e.target.value); debouncedHandleChangeProductName(e.target.value); }} /> ); }
about 4 years ago · Juan Pablo Isaza Denunciar

0

Rebotar onChange en sí mismo tiene advertencias. Digamos que debe ser un componente no controlado, ya que eliminar el rebote de onChange en un componente controlado provocaría retrasos molestos al escribir.

Otro escollo, es posible que tengamos que hacer algo inmediatamente y hacer otra cosa después de un retraso. Por ejemplo, muestre inmediatamente el indicador de carga en lugar de los resultados de búsqueda (obsoletos) después de cualquier cambio, pero envíe la solicitud real solo después de que el usuario deje de escribir.

Con todo esto en mente, en lugar de eliminar el rebote de la devolución de llamada, propongo eliminar el rebote de la sincronización a través de useEffect :

 const [text, setText] = useState(''); const isValueSettled = useIsSettled(text); useEffect(() => { if (isValueSettled) { props.onChange(text); } }, [text, isValueSettled]); ... <input value={value} onChange={({ target: { value } }) => setText(value)}

Y useIsSetlled se recuperará:

 function useIsSettled(value, delay = 500) { const [isSettled, setIsSettled] = useState(true); const isFirstRun = useRef(true); const prevValueRef = useRef(value); useEffect(() => { if (isFirstRun.current) { isFirstRun.current = false; return; } setIsSettled(false); prevValueRef.current = value; const timerId = setTimeout(() => { setIsSettled(true); }, delay); return () => { clearTimeout(timerId); } }, [delay, value]); if (isFirstRun.current) { return true; } return isSettled && prevValueRef.current === value; }

donde isFirstRun obviamente nos salva de obtener "oh, no, el usuario cambió algo" después de la representación inicial (cuando el value se cambia de undefined a valor inicial).

Y prevValueRef.current === value no es una parte requerida, pero nos asegura que obtendremos useIsSettled devolviendo false en la misma ejecución de procesamiento, no en la siguiente, solo después de ejecutar useEffect .

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda