# Objetivo Desenvolva uma aplicação web interativa de cronômetro utilizando [[React]], aproveitando o poder dos Hooks para gerenciar o estado ([[Hooks de estado no React]]) e os efeitos ([[Hooks de efeito no React]]) de tempo de forma eficiente. O objetivo é criar uma ferramenta funcional e responsiva onde o usuário possa controlar a contagem do tempo com precisão, visualizando-a em tempo real. Para isso, a aplicação deve incluir: - **Controles Essenciais:** Três botões intuitivos: "Iniciar" para começar a contagem, "Pausar" para interromper e retomar, e "Zerar" para reiniciar o cronômetro a qualquer momento. - **Exibição Clara:** O tempo decorrido precisa ser exibido em um formato legível e padronizado, como HH:MM:SS (horas, minutos, segundos), garantindo uma experiência de usuário fluida e informativa. # Solução A seguir tem-se um `App.js` para a descrição do aplicativo em questão. Lembre-se de [[Iniciando um projeto no React|Iniciar um projeto no React]]. ```jsx import React, { useState, useEffect } from 'react'; import './App.css'; function App() { // O hook 'useState' é usado para adicionar estado a um componente funcional. // Ele retorna um par: o valor do estado atual e uma função para atualizá-lo. // Aqui, 'time' guarda o valor atual do cronômetro em segundos, e 'setTime' é a função para alterar esse valor. // O valor inicial, 0, é passado como argumento para o useState. const [time, setTime] = useState(0); // Tempo em segundos // Da mesma forma, 'isRunning' é uma variável de estado que controla se o cronômetro está ativo ou pausado. // 'setIsRunning' é a função para alterar o estado de 'isRunning'. // O valor inicial é 'false', indicando que o cronômetro começa parado. const [isRunning, setIsRunning] = useState(false); // O hook 'useEffect' permite executar efeitos colaterais em componentes funcionais. // Efeitos colaterais podem ser: busca de dados, inscrições (subscriptions) ou, como neste caso, manipulação de timers. useEffect(() => { let intervalId = null; // Este efeito só será ativado se 'isRunning' for verdadeiro. if (isRunning) { // 'setInterval' é uma função do JavaScript que executa uma função repetidamente em um intervalo de tempo fixo. // Aqui, a cada 1000ms (1 segundo), a função 'setTime' é chamada para incrementar o tempo. // Usamos uma função de callback (prevTime => prevTime + 1) para garantir que estamos atualizando com base no valor mais recente do estado. intervalId = setInterval(() => { setTime(prevTime => prevTime + 1); }, 1000); } // A função de retorno do 'useEffect' é uma função de "limpeza". // Ela é executada quando o componente é desmontado ou antes de o efeito ser executado novamente. // É crucial para limpar o intervalo e evitar vazamentos de memória (memory leaks). return () => { if (intervalId) { clearInterval(intervalId); } }; }, [isRunning]); // O array de dependências. O efeito será re-executado sempre que o valor de 'isRunning' mudar. // Esta função é chamada quando o botão "Iniciar" é clicado. // Ela atualiza o estado 'isRunning' para 'true', o que ativa o 'useEffect' para iniciar o cronômetro. const handleStart = () => { setIsRunning(true); }; // Chamada pelo botão "Pausar". // Atualiza 'isRunning' para 'false', fazendo com que o 'useEffect' execute sua função de limpeza e pare o intervalo. const handlePause = () => { setIsRunning(false); }; // Chamada pelo botão "Zerar". // Para o cronômetro (se estiver rodando) e redefine o tempo para 0. const handleReset = () => { setIsRunning(false); setTime(0); }; // Função auxiliar para formatar o tempo de segundos para o formato HH:MM:SS. const formatTime = (timeInSeconds) => { const hours = Math.floor(timeInSeconds / 3600).toString().padStart(2, '0'); const minutes = Math.floor((timeInSeconds % 3600) / 60).toString().padStart(2, '0'); const seconds = (timeInSeconds % 60).toString().padStart(2, '0'); return `${hours}:${minutes}:${seconds}`; }; // O JSX que define a estrutura do componente a ser renderizada. return ( <div className="App"> <header className="App-header"> <h1>Cronômetro</h1> <div className="timer-display"> {formatTime(time)} </div> <div className="buttons"> <button onClick={handleStart} disabled={isRunning}>Iniciar</button> <button onClick={handlePause} disabled={!isRunning}>Pausar</button> <button onClick={handleReset}>Zerar</button> </div> </header> </div> ); } export default App; ``` Para melhorar a visualização, utilize o seguinte [[Cascading Style Sheets|CSS]]. ```css .App { text-align: center; } .App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } .timer-display { font-size: 3rem; margin: 20px; font-family: 'Courier New', Courier, monospace; } .buttons button { background-color: #61dafb; border: none; color: #282c34; padding: 15px 32px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; border-radius: 8px; font-weight: bold; transition: background-color 0.3s; } .buttons button:hover:not(:disabled) { background-color: #4a9eb5; } .buttons button:disabled { background-color: #cccccc; color: #666666; cursor: not-allowed; } ``` O resultado será algo semelhante ao seguinte: ![[Funcionamento App Cronômetro.gif]]