es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Por qué mi variable de ID de setInterval global no está disponible para una función?

Estoy escribiendo una aplicación de temporizador simple y usando setInterval por primera vez. El proyecto está en react typescript y uso el hook useReducer para gestionar el estado.

El proyecto requiere dos temporizadores separados, sesión y descanso, lo cual puede explicar parte del código. El proyecto también requiere un botón para iniciar y detener el temporizador, por lo tanto, estoy usando una sola función.

He redactado mi reducer y types porque el problema, según entiendo, no los involucra, sino más bien un uso simple de setInterval que aún no entiendo.

Cuando se llama a la función startStopTimer por segunda vez, intervalID es indefinido, a pesar de que se declara globalmente.

const App = () => {
    const [timerState, dispatch] = useReducer(timerStateReducer, initialTimerState
    );
};

let intervalID: NodeJS.Timer;

const startStopTimer = () => {
    let name: string = timerState.session.count !== 0 ? 'session' : 'break';

    if (timerState[name].running) {
        console.log('deteniendo temporizador' + intervalID);
        //el reducer establece la variable booleana 'running' en false
        dispatch({type: ActionKind.Stop, name: name});
        clearInterval(intervalID);
    } else {
       //el reducer establece la variable booleana 'running' en true
        dispatch({type: ActionKind.Start, name: name}); 
        intervalID = setInterval(() => {
            dispatch({type: ActionKind.Decrease, name: name});
        }, 1000);
    }
};

return (
      //código JSX redactado
      <button onclick="{startStopTimer}">Iniciar/Detener</button>
)

He intentado pasar onClick como una función de flecha (en lugar de una referencia, creo?) y se comporta de la misma manera. Intenté simplificar esto con useState, pero me encontré con otro conjunto de problemas con useState y setInterval, así que volví al hook useReducer.

¡Gracias!

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Parece que el problema es que el intervalID no se está almacenando correctamente entre las llamadas a la función. Aunque se declara de forma global, se establece como undefined cuando se vuelve a llamar a startStopTimer.

    Una posible solución es almacenar el intervalID en el estado utilizando el hook useRef, que persistirá entre las llamadas a la función. Así es como puedes modificar el código:

    const App = () => {
      const [timerState, dispatch] = useReducer(timerStateReducer, initialTimerState);
      const intervalID = useRef<nodejs.timer>();
    
      const startStopTimer = () => {
        let name: string = timerState.session.count !== 0 ? 'session' : 'break';
    
        if (timerState[name].running) {
          console.log('parando el temporizador' + intervalID.current);
          dispatch({type: ActionKind.Stop, name: name});
          clearInterval(intervalID.current);
        } else {
          dispatch({type: ActionKind.Start, name: name}); 
          intervalID.current = setInterval(() => {
            dispatch({type: ActionKind.Decrease, name: name});
          }, 1000);
        }
      };
    
      return (
        //código JSX omitido
        <button onclick="{startStopTimer}">Comenzar/Detener</button>
      )
    }
    

    Aquí, declaramos intervalID utilizando useRef<nodejs.timer>() que devuelve un objeto con una propiedad .current. Este objeto es el mismo entre renders, por lo que podemos almacenar y acceder al valor de intervalID a través de la propiedad .current.

    Cuando se llama a startStopTimer por primera vez, establecemos intervalID.current utilizando intervalID.current = setInterval(...). Cuando se llama de nuevo, podemos acceder al mismo valor de intervalID.current.</nodejs.timer></nodejs.timer>

Comments are closed.