Refactoring
Nolan Miller
Posted on July 26, 2024
Sometimes, you reach a point while you’re coding a project where you realize that the road you’ve gone down needs some correcting. And that’s where I was today.
I was starting to get overwhelmed with every change that I would have to make to implement UI logic. So, I figured… “Hm. It’s probably time to refactor my whole application.
Simple is Better
Yesterday, I talked about making my state global. So I did some research.
I could use redux… which I always have problems with when using it to conditionally render. I could use React’s baked in solution useReducer, or I could pick from the hundreds of open source state management libraries.
I decided to try Preact’s react signals library to try out signals.
It turns out, updating a signal value doesn’t re-render the component (by design). So, the first variable that I implemented wasn’t of very much use, because it tracked the active state of the Roaster
window in my Home
component.
It would open the window just fine, but it couldn’t help me move through the logic.
It was at this point that I realized… my app doesn’t have that much state.
The only piece of state that is really shared across the entire application is the current roast of the roaster — and in the future, the user (which will make sense to provide a context for).
So, today’s refactor turned into a call for simplicity.
Cleaning my Code
I noticed that a lot of my functional components carried a lot of logic in them, not just for initiating state changes, but also, functions that were doing the job of a controller.
Take my Roaster
component for instance.
The component that renders this, also rendered every page in the flow, containing all of the JSX for everything. The first change I made was turning a 100 line coniditional render into this, by exporting each possible render into it's own component:
return progress === 'start-roast-form' ? <StartRoast currentRoast={currentRoast} nextProgress={nextProgress} />
: progress === 'roast-active' ? <RoastCoffee time={time} currentRoast={currentRoast} roastStep={roastStep} handleRecordStep={handleRecordStep} nextStep={nextStep} />
: progress === 'finish-roast-form' ? <FinishRoast time={time} currentRoast={currentRoast} nextProgress={nextProgress} />
: progress === 'roast-complete' ? <RoastComplete nextProgress={nextProgress} handleSave={handleSave} />
: <></>
Yes, a much cleaner return statement.
I also created a roasterController
in an effort to make my actual roaster more readable. All it contains are the two functions that manipulate data in my roaster. These are the only two functions that will need to change about my roaster when an actual database is implemented.
import roasts from "../mocks/roasts";
import { currentRoast } from "../signals";
export const saveRoast = (roast) => {
const found = roasts.find((item, index) => {
if (item.id === currentRoast.value.id) {
return index
}
})
if (found) {
roasts[found] = currentRoast.value;
} else {
roasts.push(currentRoast.value);
}
return roasts[roasts.length - 1]
}
export const logTime = (roastStep, time) => {
let key = '';
if (roastStep === 1) key = 'firstCrackSeconds'
if (roastStep === 2) key = 'tempRiseSeconds'
if (roastStep === 3) key = 'openedLidSeconds'
if (roastStep === 4) key = 'heatOffSeconds'
if (roastStep === 5) key = 'dumpedSeconds'
currentRoast.value[key] = time
}
It’s not a large file, but removing 26 lines of logic-based code from my presentational component is a win!
More Work To Do
Optimistically, I’m looking at the refactoring that needs done and the small UI tweaks that need made, and I belive that the front end will be visually complete by this weekend!
So tomorrow, is another refactor day, followed by a cleanup of some of the remaining UI!
Check Out the Repo
If you want to keep up with the changes, fork and run locally, or even suggest code changes, here’s a link to the GitHub repo!
Posted on July 26, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.