I'll admit I'm only a year into professionally writing React but I've been working on a production codebase that was written with hardly any reusable components among other conventions that I find odd.
That said I am going to share my general philosophy on the value and importance of custom components which I picked up from a different codebase prior to working on the current one.
In the current codebase I'm working in, custom components are sparse. More-so, the UI is built with native html tags (i.e. <div>
, <span>
, etc.) in the main component (i.e. CheckoutPage). Furthermore, sometimes values are not stored in a local variable when fetched from an api call; rather rendered as:
<div className="total__price">${item.total_cents.toFixed(2)}</div>
As a one-liner it seems innocently benign but as the file grows for the entire UI on a particular feature, all you see are divs, spans, all over the place that becomes too noisy as I'm having to read className
s to begin to understand what's going on. For example....
Common markup I see on a daily:
LineItems.js
<div className="flex col space-between container">
<div className="flex line-item line-item__container">
<span>Subtotal</span>
<span>${(checkout.sub_total_cents / 100).toFixed(2)}</span>
</div>
<div className="flex line-item line-item__container">
<span>Tax</span>
<span>${(item.tax_total_cents / 100).toFixed(2)}</span>
</div>
{checkout.discount_total_cents > 0 &&
<div className="flex line-item line-item__container">
<span className="margin-right-auto">Discounts</span>
<span className="line-through">{checkout.previous_price / 100).toFixed(2)}</span>
<span>${(checkout.total_discounts_cents / 100).toFixed(2)}</span>
</div>
}
/* and many more line items ... */
</div>
When I see the above snippet, I see a few optimizations that can make this more readable and reusable should requirements change (as they usually do). Oh and I forgot to mention there is one global stylesheet for all components in the application that is 6000+ lines long with tons and tons and tons of nesting. 😔
Quick Assessment
- the line item can be a componentized
- I like to use the classnames package along with css modules
- store values in a variable
My proposed optimization:
1) Abstract the line item
LineItem.js
import {flex, space_between} from 'assets/css/flex.module.scss';
import {strikeThrough, textBold} from 'assets/css/utils.module.scss';
import {secondary} from 'components/common/text/text.module.scss';
import {formatPrice} from 'helpers';
import classnames from 'classnames';
const LineItem = ({label, value, hasDiscount, previousPrice, bold}) => {
return (
<div className={classnames(flex, space_between, {[textBold]: bold})}>
<p className={classnames({[margin_right_auto]: hasDiscount})}>{label}</p>
{hasDiscount && (
<p className={classnames(strikeThrough, secondary)}>${formatPrice(previousPrice)}</p>
)}
<p>${value}</p>
</div>
)}
export default LineItem;
2) Use the Line Item component
LineItems.js
import {flex, space_between} from 'assets/css/flex.module.scss';
import {formatPrice} from 'helpers';
import LineItem from './LineItem';
const LineItems = ({checkout}) => {
const subtotal = formatPrice(checkout?.sub_total)
const discounts = formatPrice(checkout?.discount_total_cents)
const hasDiscount = checkout?.discount_total_cents > 0
const previousPrice = formatPrice(checkout?.total_cents - checkout?.discount_total_cents)
const tax = formatPrice(checkout?.tax_cents)
const total = formatPrice(checkout.total_cents)
<div>
<LineItem label="Subtotal" value={subtotal}/>
<LineItem label="Tax" value={tax}/>
{hasDiscounts && (
<LineItem
label="Discounts"
value={discounts}
previousPrice={previousPrice}
hasDiscount
/>
)}
<LineItem label="Total" value={total} bold/>
</div>
/* and many more line items ... */
}
Why, in my opinion, this is a better approach
After componentizing the line item, reading LineItems.js
is much more clear as to what is being rendered and is noise-free. The user of LineItem
merely has to look at what LineItem
expects (props)
or look at how it's currently being used to simply add a new Service Charge line item should The Product team wish to add it to "Checkout" AND the "Order Receipt".
Conclusion
Bear in mind – this is a simplified code snippet to get my point across. I understand some people are of the school of thought that abstracting the line item to a reusable component is over abstracting; I don't believe so. It is my belief that once the component is created and well-defined, any new dev that joins the team can easily understand what LineItems
is rendering and add a new line item LineItem.js
within a matter of seconds. Efficiency..... Efficiency is the value and importance of this approach.
Please let me know what you guys think. Is this over abstracting or do you think this is good practice? Also, are you still using Class components versus Functional Components in 2021? Let me know. Thanks.
Top comments (0)