Quería crear una aplicación de temporizador en React usando functional component y a continuación se encuentran los requisitos.
El componente mostrará un número inicializado en 0 conocido como counter .
El componente mostrará un botón de Start debajo del número del counter .
Al hacer clic en el botón Start , el contador comenzará a ejecutarse. Esto significa que el número del counter comenzará a incrementarse en 1 por cada segundo.
Cuando el contador está funcionando (aumentando), el botón Start se convertirá en el botón Pause .
Al hacer clic en el botón Pause , el counter conservará su valor (número) pero dejará de funcionar (incrementarse).
El componente también mostrará un botón Reset . Al hacer clic en el botón Reset , el counter volverá a su valor inicial (que es 0 en nuestro caso) y dejará de funcionar (aumentando).
A continuación se muestra el código que he implementado, pero parece que clearInterval no funciona. Además, ¿cómo implemento el botón Restablecer?
Código:
import React, { useState, useEffect } from "react"; export default function Counter() { const [counter, setCounter] = useState(0); const [flag, setFlag] = useState(false); const [isClicked, setClicked] = useState(false); var myInterval; function incrementCounter() { setClicked(!isClicked); if (flag) { myInterval = setInterval( () => setCounter((counter) => counter + 1), 1000 ); setFlag(false); } else { console.log("sasdsad"); clearInterval(myInterval); } } function resetCounter() { clearInterval(myInterval); setCounter(0); } useEffect(() => { setFlag(true); }, []); return ( <div> <p>{counter}</p> <button onClick={incrementCounter}> {isClicked ? "Pause" : "Start"} </button> <button onClick={resetCounter}>Reset</button> </div> ); }Enlace de Codesandbox: CodeSandbox
Tienes que almacenar myInterval en state. Después de eso, cuando se hace clic en el botón y flag es false , puede borrar el intervalo (myInterval in state).
Use useRef para hacer que el intervalo sea una referencia. Luego use resetCounter() para limpiar el intervalo ref.
const intervalRef = useRef(null) const incrementCounter = () => { intervalRef.current = setInterval(() => { setCounter(prevState => prevState + 1) }, 1000); }; const resetCounter = () => { clearInterval(intervalRef.current); intervalRef.current = null; };Hice una versión ligeramente diferente que usa un useEffect adicional que se ejecuta en isRunning (cambió el nombre de flag ):
import React, { useState, useEffect, useRef } from "react"; export default function Counter() { const [counter, setCounter] = useState(0); // Change initial value to `false` if you don't want // to have timer running on load // Changed `flag` name to more significant name const [isRunning, setIsRunning] = useState(false); // You don't need 2 variable for this //const [isClicked, setClicked] = useState(false); // Using `useRef` to store a reference to the interval const myInterval = useRef(); useEffect(() => { // You had this line to start timer on load // but you can just set the initial state to `true` //setFlag(true); // Clear time on component dismount return () => clearInterval(myInterval.current); }, []); // useEffect that start/stop interval on flag change useEffect(() => { if (isRunning) { myInterval.current = setInterval( () => setCounter((counter) => counter + 1), 1000 ); } else { clearInterval(myInterval.current); myInterval.current = null; } }, [isRunning]); // Now on click you only change the flag function toggleTimer() { setIsRunning((isRunning) => !isRunning); } function resetCounter() { clearInterval(myInterval.current); myInterval.current = null; setCounter(0); setIsRunning(false); } return ( <div> <p>{counter}</p> <button onClick={toggleTimer}>{isRunning ? "Pause" : "Start"}</button> <button onClick={resetCounter}>Reset</button> </div> ); }Demostración:https://codesandbox.io/s/dank-night-wwxqz3?file=/src/Counter.js
Como un pequeño extra, hice una versión que usa un gancho personalizado useTimer . De esta forma, el código del componente es mucho más limpio: https://codesandbox.io/s/agitated-curie-nkjf62?file=/src/Counter.js