Hey! I'm on a mission to make 100 React.js projects ending March 31st. Please follow my dev.to profile or my twitter for updates and feel free to reach out if you have questions. Thanks for your support!
Link to the deployed project: Link
Link to the repo: github
So, I'm not going to lie to you. I completely stole the Javascript for this project from a recent Dev.to post by programmer and rising Youtuber Aleks Popovic- here's his recent article, and here's his Youtube channel which you should follow :)
The thing is, I've made little Pomodoro apps before (in pure html and JS), and like what happens so often in programming- sometimes you do something that gives you a headache and you just never want to do it again. For me, that was figuring out how to perform the Javascript countdown function and output a standard clock format for the minutes and seconds like we have here. I've wanted to build a full-featured Pomodoro application in React for a while so having another, more venerable programmer create the most important clockwork allows you to piggy-back and build other useful features on top of it. Also Aleks' Javascript is more elegant and concise than my original Pomodoro was so it's nice to practice with.
Today I give you the Popovic Pomodoro. It consists of a bit of styling, an App
component and a Pomodoro
component. I also gave mine a header. We'll go over the Pomodoro
component in depth since that's where the real action is.
First, we import useState
and useEffect
into our functional component and create three pieces of state- minutes
, seconds
, and displayImage
.
import React,{useState,useEffect} from 'react'
function Pomodoro() {
const [minutes,setMinutes] = useState(24);
const [seconds,setSeconds] = useState(59);
const [displayMessage,setDisplayMessage] = useState(false);
return () {...}
}
Next, inside our return statement we write the structure of our component's JSX. We have a div with the class "message" for our message and a div with the class "timer" for all of the timer action.
return (
<div className='pomodoro'>
<div className='message'>
{displayMessage && <div>Break time! New session starts in:</div>}
</div>
<div className='timer'>
:
</div>
</div>
)
As you can see, we only display the message text if displayMessage
is true. This is done with conditional rendering.
For the timer, we knew we have to display a human-readable clock in the standard format, which is not the format that computers display numbers. For example, if there is 1 minute and 1 second left on the timer, it must be displayed as "1:01" rather than the computer's preferred "1:1". To do that we define variables for this before the return statement and then call them in the timer
element.
const timerMinutes = minutes < 10 ? `0${minutes}` : minutes;
const timerSeconds = seconds < 10 ? `0${seconds}` : seconds;
return (
...
<div className='timer'>
{timerMinutes}:{timerSeconds}
</div>
)
Ok, lastly, for the countdown functionality, we will have to use useEffect
to re-render the component only under select conditions. We will also have to use the native Javascript setInterval() method to create the timer. It isn't perfectly accurate but it's usually within about a second of accuracy for a 25 minute timer, which is good enough for me.
useEffect(() => {
let interval = setInterval(() => {
clearInterval(interval);
if (seconds === 0) {
if (minutes !== 0) {
setSeconds(59);
setMinutes(minutes - 1);
} else {
let minutes = displayMessage ? 24 : 4;
let seconds = 59;
setSeconds(seconds);
setMinutes(minutes);
setDisplayMessage(!displayMessage);
}
} else {
setSeconds(seconds - 1);
}
}, 1000)
},[seconds,minutes,displayMessage])
In plain English (or verbal pseudo-code) what this is doing is creating an interval that runs every 1000 milliseconds or 1 second. At the end of each second we clear the interval from running. This is really important because without clearing the interval the browser will keep making new ones as the old ones are still running and then your timer will start jumping around, and you'll have a memory leak.
Then, if seconds are zero, we either restart the seconds timer back at 59 to count down another minute, or if minutes
is equal to zero, we set the displayMessage
bool to true and start the break timer. This is all done with a series of clever ternaries that keep the Pomodoro operation short. If seconds are not zero, then we remove 1 from seconds
and since that piece of state is displayed on screen, it keeps reducing the counter the user sees by 1 second.
I'll follow up on this project later in the week to build out some extra functionality, but for today, there's your Popovic Pomodoro!
As always, please follow me on Twitter for additional updates and valuable shares from other developers. Also follow Aleks, since he's the creator of this little gem- his blog is here.
Top comments (3)
I also had a hard time building a pomodoro timer!
I decided to go plain JS/HTML/CSS because of re-learning the basics.
I do want to build it again in ReactJS in the near future so this post is very helpful.
Yeah! Check out Aleks Popovic's video that's linked in my post and you'll be building pomodoros in no time.
Thank you for your kind words James. :) Good luck on your 100 projects journey!