This morning, while browsing through my old GitHub repositories, I came across a component from my first ReactJS project and I noticed a small mistake. I'll let you guess what it is before giving you the answer.
Here's the component:
function FoodAlert() {
const {data, isLoading} = useData();
return <Alert>
<AlertTitle>Too much</AlertTitle>
<AlertDescription>
<h2>It seems to be enough</h2>
<div>
{data ? <h3>you put on {data.surplus} grams too much</h3> : <NoDataScreen />}
{isLoading && <Skeleton />}
</div>
</AlertDescription>
</Alert>
}
The mistake is that if the isLoading
expression is true, the NoDataScreen
component will be displayed along with the skeleton, because if isLoading
is true, data
is implicitly evaluated as false.
It's not a big deal, let's fix it:
function FoodAlert() {
const {data, isLoading} = useData();
return <Alert>
<AlertTitle>Too much</AlertTitle>
<AlertDescription>
<h2>It seems to be enough</h2>
<div>
// that's it
{data && !isLoading ?
<h3>you put on {data.surplus} grams too much</h3>
:
<NoDataScreen />}
{isLoading && <Skeleton />}
</div>
</AlertDescription>
</Alert>
}
Now, we’ve certainly fixed the bug, but don’t you think the component is a bit too verbose?
For my cooking recipe app, which is purely for educational purposes, it’s not really worth giving in to this kind of reasoning, I admit. But let’s say it’s no longer a recipe app but a large banking UI that needs to be easily scalable.
Component composition + early return = the magic formula
You might have expected me to talk about a revolutionary method that would solve our situation, but not at all. The solution has always been there; it’s about breaking down your UI into components. This is one of the fundamentals of the ReactJS library philosophy. You can explore the topic Right here.
In our case, I divided the FoodAlert
component into two:
Here's what it looks like in code:
function Layout({children}) {
return <Alert>
<AlertTitle>Too much</AlertTitle>
<AlertDescription>
<h2>It seems to be enough</h2>
<div>{children}</div>
</AlertDescription>
</Alert>
}
function FoodAlert() {
const {data, isLoading} = useData();
return <Layout>
{data && !isLoading ?
<h3>you put on {data.surplus} grams too much</h3>
:
<NoDataScreen />}
{isLoading && <Skeleton />}
</Layout>
}
And that's it for this first step of refactoring. We still haven't addressed the verbosity issue caused by the overuse of conditional rendering.
Better done now than later
This is where the early return comes in to save us from headaches. Here's how it translates into code:
function FoodAlert() {
const {data, isLoading} = useData();
if(isLoading) {
return <Layout>
<Skeleton />
</Layout>
}
if(!data) {
return <Layout>
<NoDataScreen />
</Layout>
}
return <Layout>
<h3>you put on {data.surplus} grams too much</h3>
</Layout>
The component is easier to read, isn't it?
Top comments (0)