Isolating third party frontend components
tl;dr
Put all third party components inside wrapper components that you create yourself. This decreases coupling, and might also improve readability and specificity.
function View() {
return <MyOwnCheckbox title="Opt-in" />
}
/* Hide away dependency on BootstrapFormControl */
function MyOwnCheckbox(props) {
return <div class="my-own-checkbox">
<span>{props.title}</span>
<BootstrapFormControl
type="checkbox"
size-adjustment="5"
color-scheme="cyan"
obnoxious-attribute="arthur-dent"
/>
</div>;
}
The problem
Front-end is a fast moving landscape of technologies and add-ons. You will always be one step behind the latest version of library X or component Y.
The npm registry does keep all versions of software, so you might think to just stay on an old version. This runs into two problems:
- You'll miss out on any bug fixes, security- and feature updates.
- Documentation for old versions is usually removed. This makes further development very difficult.
This project seem to have BoostrapFormControl all over the place. I wonder what it does, and how to use it? The documentation disappeared two years ago...
Learning from backend developers
In backend systems, the repository pattern is common. Instead of having your main application code directly calling the database with sql-commands, a repository class is created. The repository class does all the sql-handling.
Using repository pattern
Without repository pattern
Even in a frontend project, you might have classes such as APIService or DataFetcher rather than calling fetch directly from your views or components.
Revisiting frontend
I don't know if it is the legacy of jQuery, but frontend code often tends to look like this:
function View() {
return (<div>
<p>Text, whatever</p>
<WickedButtonControl
type="color-picker"
included-colors="red, green, magentayellow"
size=40
margins="20,42,02,13"
some-other-stuff="true"
do-the-smurf-setting="smurf" />
</div>);
}
Here, the view is dependent on WickedButtonControl. This is the equivalent of coding directly against SqlConnection in the backend example.
What I suggest is to not use the third party WickedButtonControl class directly in your view. Instead, wrap it inside your own component:
function View() {
return (<div>
<p>Some info, whatever</p>
<MyColorPicker colors="red, green, magentayellow" />
</div>);
}
function MyColorPicker(props) {
return <WickedButtonControl
type="color-picker"
included-colors={props.colors}
size=40
margins="20,42,02,13"
some-other-stuff="true"
do-the-smurf-setting="smurf" />
}
Easier to change
Let's say we want to remake the color picker, perhaps replace it with another library. Even if this new library expects different inputs, we don't have to change the views. We only have to change our own wrapper-class: MyColorPicker.
/* The same view code as before. */
function View() {
return (<div>
<p>Some info, whatever</p>
<MyColorPicker colors="red, green, magentayellow" />
</div>);
}
function MyColorPicker(props) {
/*
We are going to use BootstrapColorPicker,
which expects hexadecimal color codes instead of color names
which were previously used.
*/
let colorCodes = props.colors
.split(",")
.map(x => x.trim()) /* Remove any blank spaces */
.map(colorNameToColorCode)
return <BootstrapColorPicker
colors={colorCodes}
theme="whatever-some-theme" />;
}
function colorNameToColorCode(name) {
if(name == "red") {
return "#ff0000";
} else if(name == "green") {
return "#00ff00";
} /* etc. */
return "#ffffff";
}
Notice that just by looking at the usage and the props, we can get a pretty good idea of what the component is supposed to do, and what we have to change for the new implementation.
The props act as a specification telling you what is expected of MyColorPicker.
Reduced complexity
Let's say you are using BootstrapPopup, and that BootstrapPopup has 45 different possible settings and props. How many of these will you be using? How many will be relevant for the view code?
By inserting BootstrapPopup directly into your view, you are dragging in the complexity of 45 different possible settings. This makes you view harder to maintain.
Probably, your view only cares about a few things. Like popup text, title and icon. This is a great foundation for defining your own component:
function View() {
return <MyOwnPopup title="hi" icon="warning" message="Welcome to the popup!" />;
}
function MyOwnPopup(props) {
let iconIndex = props.icon == "warning" ? 1 : 0;
return <BootstrapPopup
title={props.title}
icon={iconIndex}
message={props.text}
padding=20
some-other-specific-option=true
/>;
}
Notice how the view does not need to care about specific formatting or bootstrap settings. We have reduced the complexity of the view's code.