DEV Community

Nat Ravenhill
Nat Ravenhill

Posted on

4

Refactoring the Pomodoro Timer: Components

Previously I wrote a Pomodoro timer app. The code is contained in a single React component. This is generally bad practice - we want components to each be responsible for a single bit of functionality and be reusable, so we will we split out functionality into smaller components.

First I made a folder called "components", which will house the sub components and their styles.

There are 3 elements we can immediately split out:

  1. The main timer and button ("Tomato" element)
  2. The Break length controls
  3. The Session length controls

image

Break Length

This section has a breakLength value contained in the state and two button handler methods to increase/decrease this value.

First, we create a new component called BreakLength and paste the JSX into the render method. As we are now in a child component, and React has unidirectional data flow, we cannot update the breakLength value in the parent component from the BreakLength component.

Therefore, we pass a reference to the method into the BreakLength component as a property and call this in a new onClick handler. This will call the parent method and update its state.

The breakLength value is passed to the new component as a child property. When the state is changed in the parent, this will change the property value and re-render the BreakLength component.

class BreakLength extends Component {
handleBreakIncrement = () => {
this.props.handleBreakIncrement();
};
handleBreakDecrement = () => {
this.props.handleBreakDecrement();
};
render() {
return (
<div className="Break-length">
<h2 id="break-label">Break Length</h2>
<h3 id="break-length">{this.props.breakLength}</h3>
<button
id="break-decrement"
onClick={this.handleBreakDecrement.bind(this)}
>
<FontAwesomeIcon icon={faMinusCircle} size="2x" />
</button>
<button
id="break-increment"
onClick={this.handleBreakIncrement.bind(this)}
>
<FontAwesomeIcon icon={faPlusCircle} size="2x" />
</button>
</div>
);
}
}
view raw BreakLength.jsx hosted with ❤ by GitHub

Session Length

This is refactored in the same way:

class SessionLength extends Component {
handleSessionIncrement = () => {
this.props.handleSessionIncrement();
};
handleSessionDecrement = () => {
this.props.handleSessionDecrement();
};
render() {
return (
<div className="Session-length">
<h2 id="session-label">Session Length</h2>
<h3 id="session-length">{this.props.sessionLength}</h3>
<button
id="session-decrement"
onClick={this.handleSessionDecrement.bind(this)}
>
<FontAwesomeIcon icon={faMinusCircle} size="2x" />
</button>
<button
id="session-increment"
onClick={this.handleSessionIncrement.bind(this)}
>
<FontAwesomeIcon icon={faPlusCircle} size="2x" />
</button>
</div>
);
}
}

Tomato

The Tomato has 2 functions - handleStartStop and handleReset. Again, we pass references to these as properties and call them in the onClick handlers in the child Tomato component. The minutes and second properties are passed as props to the Tomato component.

class Tomato extends Component {
handleStartStop = () => {
this.props.handleStartStop();
};
handleReset = () => {
this.props.handleReset();
};
render() {
return (
<div className="tomato">
<p id="time-left">
{this.props.minutes.toString().padStart(2, "0")}:
{this.props.seconds.toString().padStart(2, "0")}
</p>
<button id="start_stop" onClick={this.handleStartStop.bind(this)}>
<FontAwesomeIcon icon={faPlay} color="#FDE9B0" size="2x" />
<FontAwesomeIcon icon={faPause} color="#FDE9B0" size="2x" />
</button>
<button id="reset" onClick={this.handleReset.bind(this)}>
<FontAwesomeIcon icon={faRedo} color="#FDE9B0" size="2x" />
</button>
</div>
);
}
}
view raw Pomodoro Tomato hosted with ❤ by GitHub

Now our main component is much smaller and we have a better separation of concerns. We can also extract some of the styles in Session.css that only apply to the Tomato into a new CSS file and include it just in this component.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (1)

Collapse
 
thomasstep profile image
Thomas Step

I wonder if you could reduce the BreakLength and SessionLength components down into a single component as well. They seem to perform similar actions. Maybe the text in the h2 tag and the handle*Decrement/handle*Increment functions can all be passed in as props.

Do you have this hosted anywhere?

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay