Solution to Design Challenge 009: Two Ways to Refactor a Status-Driven UI in React
Component extraction vs. helper refactor — which scales better?
If you're new to this series, here's a quick recap: in each Design Challenge, I present a real-world frontend problem (usually React-related), and a week later I follow up with the refactoring or solution I applied.
In last week's challenge, we looked at a component called DirectToBoot
that changes its UI based on different order statuses — from loading, to ready for pickup, to error handling. This week, I'll walk you through two practical approaches to refactor that logic.
This example is part of the newly updated edition of my book, Maintainable React — if you haven’t checked it out yet, you can use that link for a 50% discount.
The original implementation worked — but it was cluttered, with conditional checks scattered across the component. In this issue, I’ll walk you through two practical ways to refactor that code to make it more maintainable and scalable.
🧱 Option 1: Extract Helper Structures
This approach is the simplest: keep the main structure but extract the pieces of logic that are repeated or too verbose inline.
✅ Step 1: Extract a statusMessages
map
const statusMessages: Record<Status, string> = {
initialised: "We're preparing your order...",
ready: "Please click the button when you have arrived...",
notified: "Thanks for letting us know...",
error: "Something went wrong. Please call the number below.",
};
✅ Step 2: Extract an ActionButton
component
function ActionButton({ status, onNotify }) {
switch (status) {
case "initialised":
return <button className="primaryButton" disabled>I'm here</button>;
case "ready":
return <button className="primaryButton" onClick={onNotify}>I'm here</button>;
case "error":
return <a href="tel:042333" className="primaryButton">Call us</a>;
default:
return null;
}
}
🧼 Cleaner JSX
export function DirectToBoot({ orderId }: { orderId: string }) {
const [status, setStatus] = useState<Status>("initialised");
const handleNotify = () => {
mutation.mutate(orderId);
};
return (
<div className="container">
<h3>Direct To Boot</h3>
<p>{statusMessages[status]}</p>
<ActionButton status={status} onNotify={handleNotify} />
</div>
);
}
This keeps the logic inline but improves readability. You still work in one file, but without burying the core UI in conditionals.
🧩 Option 2: One Component Per Status
This second approach fully separates the logic for each status into its own component.
Keep reading with a 7-day free trial
Subscribe to The Pragmatic Developer to keep reading this post and get 7 days of free access to the full post archives.