Pido disculpas si esto se ha preguntado antes, pero todavía no he encontrado nada que me ayude, ¡y soy nuevo en React!
Quiero un valor para aumentar cada n número de segundos que el usuario mantiene presionado un botón. ¡En este momento está aumentando linealmente cada 35 milisegundos!
Hasta ahora tengo algo como esto:
function Increaser() { const [value, setValue] = useState(0); const changeTimer: React.MutableRefObject<any> = useRef(null); function timeoutClearUp() { clearInterval(changeTimer.current); } function increment() { changeTimer.current = setInterval(() => setValue(prev => prev + 1), 35); }; function onChange(e: any) { setValue(parseInt(e.target.value)); } return ( <div> <button onMouseUp={timeoutClearUp} onMouseDown={increment}>INCREASE</button> <input type="number" value={value} onChange={(e) => {onChange(e)}} /> </div> ); } ¡He intentado agregar otra ref pero no parece funcionar! ¿Qué es una pequeña cosa que puedo agregar a este código para garantizar que cada segundo value se incremente en un valor cada vez mayor (cuanto más tiempo mantenga presionado el botón el usuario).
¡Muchas gracias!
Debido a que nadie le dio una solución real, aquí hay una forma de hacer lo que quiere.
Lo que he hecho aquí es usar una referencia a la entrada y actualizar el valor en su estado una vez que dejan de hacer clic en el botón de incremento o cuando cambian la entrada manualmente.
En la función de incremento, uso setTimeout para crear un intervalo personalizado, y este intervalo usa una ecuación matemática para determinar cuál será el próximo tiempo de espera/intervalo. La ecuación que utilicé pasará de 35 ms (inicio) a 10 ms (final) en 41 segundos y no bajará de 10 ms.
Aquí hay un gráfico de tiempo de lo que uso en este ejemplo para acelerar el intervalo. (el eje x es el tiempo en segundos y el eje y es el retraso en milisegundos) 
import { useState, useRef } from 'react'; function Increaser() { const [value, setValue] = useState(0); const myInputRef = useRef<HTMLInputElement>(null); let myTimeout: ReturnType<typeof setTimeout>; let startTime: Date; function stopInterval() { clearTimeout(myTimeout!);//Stop the interval in essence setValue(parseInt(myInputRef.current!.value!)||0) //Set the value of the input (in the mean time value is "floating") } function increment() { startTime = new Date();//Initialize when we started increasing the button myTimeout = setTimeout(function runIncrement() {//Start the timeout which is really a controlled interval const val = (parseInt(myInputRef!.current!.value)||0)+1;//Increment the value of the ref's target myInputRef!.current!.value = (val).toString()//Assign that incremented value to the ref const now = new Date(); const delta = now.getTime() - startTime.getTime(); const n = 10 + (1/(Math.pow(1.1,delta/1000-(2*Math.log(5))/Math.log(1.1))));//Whatever //The math function I have chosen above will go from 35 ms to 10 ms in 41 seconds. //The function will not go lower than 10 ms and starts at 35 ms //However you can use whatever function or math you want to get it to do as you wish. //... //whatever logic here you want to calculate when we will trigger the increment again. //... myTimeout = setTimeout(runIncrement, parseInt(n.toString()));//Increment again after the variable n milliseconds which we calculate above },35);//Start the interval with an initial time of 35 milliseconds //Now we know when the interval started, and we can }; function onChange(e: any) { setValue(parseInt(e.target.value)); } return ( <div> <button onMouseUp={stopInterval} onMouseDown={increment}>INCREASE</button> <input type="number" onChange={(e) => {onChange(e)}} ref={myInputRef} /> </div> ); } export default Increaser;`
Me disculpo por el ligero formato que SO está haciendo aquí (en combinación con mi editor de código VS).
Gracias a la respuesta de SharpInnovativeTechnologies, me di cuenta de lo tonto que era y encontré una solución fácil:
function increment() { const past = Date.now() const rate = 500; changeTimer.current = setInterval(() => { const diff = Math.floor((Date.now() - past) / rate); setValue(prev => prev + (1 + diff)) }, 50); } Donde rate es una especie de ajuste de sensibilidad.
Entonces, cada 50 ms, The Date.now() - past aumenta y aumenta el value con el setter.
Si quieres crecer cada segundo, debes cambiar
... changeTimer.current = setInterval(() => setValue(prev => prev + 1), 35); ... <input type="number" value={value} />a
... changeTimer.current = setInterval(() => setValue(prev => prev + 35), 35); ... <input type="number" value={value/1000} /> porque el segundo parámetro de setInterval es milisegundos.
import React, { useState, useRef } from "react"; export default function Increaser() { const [value, setValue] = useState(0); const changeTimer = useRef(null); function timeoutClearUp() { if (changeTimer.current) { clearInterval(changeTimer.current); changeTimer.current = null; } } function increment() { changeTimer.current = setInterval(() => { setValue((prev) => prev + 35); }, 35); } return ( <div> <button onMouseUp={timeoutClearUp} onMouseDown={increment}> INCREASE </button> <button onClick={() => { setValue(0); }} > Reset </button> <div>{value / 1000}</div> </div> ); }