DEV Community

loading...
Cover image for Project 51 of 100 - Everyone Needs to Build a Pomodoro

Project 51 of 100 - Everyone Needs to Build a Pomodoro

jwhubert91 profile image James Hubert ・4 min read

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 () {...}
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
Enter fullscreen mode Exit fullscreen mode

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>
)
Enter fullscreen mode Exit fullscreen mode

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])
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (3)

pic
Editor guide
Collapse
morencyleysner profile image
Morency "Venom" Leysner

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.

Collapse
jwhubert91 profile image
James Hubert Author

Yeah! Check out Aleks Popovic's video that's linked in my post and you'll be building pomodoros in no time.

Collapse
alekswritescode profile image
Aleks Popovic

Thank you for your kind words James. :) Good luck on your 100 projects journey!