上QQ阅读APP看书,第一时间看更新
Pomodoro timer – implementing the constructor and componentDidMount
To understand componentDidMount, we are going to create a Pomodoro Timer (if you don't know what it is you can read this: https://en.wikipedia.org/wiki/Pomodoro_Technique).
Our Pomodoro timer will look like this:
Creating our Pomodoro Timer:
- The first thing we need to do is to create a new folder called Pomodoro in our components directory, as well as a file called Timer.js and the CSS file, Timer.css. This is the skeleton of the class component we will use for this component:
import React, { Component } from 'react';
import './Timer.css';
class Timer extends Component {
constructor() {
super();
}
componentDidMount() {
}
render() {
return (
<div className="Pomodoro">
</div>
);
}
}
export default Timer;
File: src/components/Pomodoro/Timer.js
- For our Pomodoro timer, we need to initialize our local state in the constructor with some values for the time and for the alert (when the time is over):
constructor() {
super();
// Initial State
this.state = {
alert: {
type: '',
message: ''
},
time: 0
};
// Defined times for work, short break and long break...
this.times = {
defaultTime: 1500, // 25 min
shortBreak: 300, // 5 min
longBreak: 900 // 15 min
};
}
- The componentDidMount method is called once the component is mounted and is executed just once. In this case, once our component is mounted we need to update our time state with the default time (25 min), and to do this, we need to create a new method called setDefaultTime and then execute it in our componentDidMount method:
componentDidMount() {
// Set default time when the component mounts
this.setDefaultTime();
}
setDefaultTime = () => {
// Default time is 25 min
this.setState({
time: this.times.defaultTime
});
}
- After we defined our default time to our time state, let's see how we need to render the Pomodoro Timer. Our render method should look like this:
render() {
const { alert: { message, type }, time } = this.state;
return (
<div className="Pomodoro">
<div className={`alert ${type}`}>
{message}
</div>
<div className="timer">
{this.displayTimer(time)}
</div>
<div className="types">
<button
className="start"
onClick={this.setTimeForWork}
>
Start Working
</button>
<button
className="short"
onClick={this.setTimeForShortBreak}
>
Short Break
</button>
<button
className="long"
onClick={this.setTimeForLongBreak}
>
Long Break
</button>
</div>
</div>
);
}
- In this case, our JSX is very simple. We are getting the values from the local state (message, type, and time) and displaying a div to show our alert when the user receives an alert message. We have another div to show our timer, and here we are passing our current time (expressed in seconds) to the displayTimer method, which will convert those seconds into mm:ss format. The last piece of the layout are the buttons to select the type of timer (start working for 25 min, short break for 5 min, or long break for 15 min), and you may have noticed that we are executing different methods on the onClick event for each type of timer.
- setTimeForWork, setTimeForShortBreak, and setTimeForLongBreak: The purpose of these three functions is to update the alert message depending on the type of the timer and then call a common function called setTime, passing as a parameter the specific time for each option. Let's first see what these three functions should look like:
setTimeForWork = () => {
this.setState({
alert: {
type: 'work',
message: 'Working!'
}
});
return this.setTime(this.times.defaultTime);
}
setTimeForShortBreak = () => {
this.setState({
alert: {
type: 'shortBreak',
message: 'Taking a Short Break!'
}
});
return this.setTime(this.times.shortBreak);
}
setTimeForLongBreak = () => {
this.setState({
alert: {
type: 'longBreak',
message: 'Taking a Long Break!'
}
});
return this.setTime(this.times.longBreak);
}
- As we learned in the previous recipes when we specify our methods with arrow functions in our class they are automatically bound (they have access to the "this" object). That means we don't need to bind them on the constructor. Now let's create our setTime method:
setTime = newTime => {
this.restartInterval();
this.setState({
time: newTime
});
}
- As you can see, we executed a new method called restartInterval(), and we updated our local state with the newTime variable, which we passed as a parameter (it can be 1,500 seconds = 25 min, 300 seconds = 5 min or 900 seconds = 15 min). You probably noticed, from the name of the function, that we are going to use a setInterval function, which is used to call a function every X milliseconds. Our restartInterval function should be like this:
restartInterval = () => {
// Clearing the interval
clearInterval(this.interval);
// Execute countDown function every second
this.interval = setInterval(this.countDown, 1000);
}
- In this case, we first cleared our interval with clearInterval(this.interval). This is because the user can switch between the different types of the timer, so we need to clear the interval each time we set a new timer. After we cleared the interval, then we call the countDown function every second using setInterval. The countDown function is as follows:
countDown = () => {
// If the time reach 0 then we display Buzzzz! alert.
if (this.state.time === 0) {
this.setState({
alert: {
type: 'buz',
message: 'Buzzzzzzzz!'
}
});
} else {
// We decrease the time second by second
this.setState({
time: this.state.time - 1
});
}
}
- The last piece of this puzzle is the displayTimer function, which will convert the time into an mm:ss format and display it in our component:
displayTimer(seconds) {
// Formatting the time into mm:ss
const m = Math.floor(seconds % 3600 / 60);
const s = Math.floor(seconds % 3600 % 60);
return `${m < 10 ? '0' : ''}${m}:${s < 10 ? '0' : ''}${s}`;
}
- Let's put it all together:
import React, { Component } from 'react';
import './Timer.css';
class Timer extends Component {
constructor() {
super();
// Initial State
this.state = {
alert: {
type: '',
message: ''
},
time: 0
};
// Defined times for work, short break and long break...
this.times = {
defaultTime: 1500, // 25 min
shortBreak: 300, // 5 min
longBreak: 900 // 15 min
};
}
componentDidMount() {
// Set default time when the component mounts
this.setDefaultTime();
}
setDefaultTime = () => {
// Default time is 25 min
this.setState({
time: this.times.defaultTime
});
}
setTime = newTime => {
this.restartInterval();
this.setState({
time: newTime
});
}
restartInterval = () => {
// Clearing the interval
clearInterval(this.interval);
// Execute countDown every second
this.interval = setInterval(this.countDown, 1000);
}
countDown = () => {
// If the time reach 0 then we display Buzzzz! alert.
if (this.state.time === 0) {
this.setState({
alert: {
type: 'buz',
message: 'Buzzzzzzzz!'
}
});
} else {
// We decrease the time second by second
this.setState({
time: this.state.time - 1
});
}
}
setTimeForWork = () => {
this.setState({
alert: {
type: 'work',
message: 'Working!'
}
});
return this.setTime(this.times.defaultTime);
}
setTimeForShortBreak = () => {
this.setState({
alert: {
type: 'shortBreak',
message: 'Taking a Short Break!'
}
});
return this.setTime(this.times.shortBreak);
}
setTimeForLongBreak = () => {
this.setState({
alert: {
type: 'longBreak',
message: 'Taking a Long Break!'
}
});
return this.setTime(this.times.longBreak);
}
displayTimer(seconds) {
// Formatting the time into mm:ss
const m = Math.floor(seconds % 3600 / 60);
const s = Math.floor(seconds % 3600 % 60);
return `${m < 10 ? '0' : ''}${m}:${s < 10 ? '0' : ''}${s}`;
}
render() {
const { alert: { message, type }, time } = this.state;
return (
<div className="Pomodoro">
<div className={`alert ${type}`}>
{message}
</div>
<div className="timer">
{this.displayTimer(time)}
</div>
<div className="types">
<button
className="start"
onClick={this.setTimeForWork}
>
Start Working
</button>
<button
className="short"
onClick={this.setTimeForShortBreak}
>
Short Break
</button>
<button
className="long"
onClick={this.setTimeForLongBreak}
>
Long Break
</button>
</div>
</div>
);
}
}
export default Timer;
File: src/components/Pomodoro/Timer.js
- After we have completed our component, the last step is to add our styles. This is the CSS used for the Pomodoro timer. Of course, you can change it if you prefer:
.Pomodoro {
padding: 50px;
}
.Pomodoro .timer {
font-size: 100px;
font-weight: bold;
}
.Pomodoro .alert {
font-size: 20px;
padding: 50px;
margin-bottom: 20px;
}
.Pomodoro .alert.work {
background: #5da423;
}
.Pomodoro .alert.shortBreak {
background: #f4ad42;
}
.Pomodoro .alert.longBreak {
background: #2ba6cb;
}
.Pomodoro .alert.buz {
background: #c60f13;
}
.Pomodoro button {
background: #2ba6cb;
border: 1px solid #1e728c;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset;
color: white;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-weight: bold;
line-height: 1;
margin: 50px 10px 0px 10px;
padding: 10px 20px 11px;
position: relative;
text-align: center;
text-decoration: none;
}
.Pomodoro button.start {
background-color: #5da423;
border: 1px solid #396516;
}
.Pomodoro button.short {
background-color: #f4ad42;
border: 1px solid #dd962a;
}
File: src/components/Pomodoro/Timer.css
Don't forget to import the <Timer /> component into App.js. If you follow everything correctly, you should see the Pomodoro timer working like this:
- Working:
- Taking a short break:
- Taking a long break:
- Buzzzz - time over!:
I challenge you to add a Play, Pause, and Reset buttons to control the timer.