205
showing only iphone [see all]
Introducing the Single Element Pattern – freeCodeCamp
CSS React components styled-components JavaScript programming
Introducing the Single Element Pattern
Rules and best practices for creating reliable building blocks with React and other component-based libraries.
Diego HazJun 20
Back in 2002 — when I started building stuff for the web — most developers, including me, structured their layouts using <table> tags.
Only in 2005 did I start following web standards.
When a web site or web page is described as complying with web standards, it usually means that the site or page has valid HTML, CSS and JavaScript. The HTML should also meet accessibility and semantic guidelines.
I learned about semantics and accessibility, then started to use proper HTML tags and external CSS. I was proudly adding those W3C Badges to every web site I made.
The HTML code we wrote was pretty much the same as the output code that went to the browser. That means that validating our output using the W3C Validator and other tools was also teaching us how to write better code.
Time has passed. To isolate reusable parts of the front end, I've used PHP, template systems, jQuery, Polymer, Angular, and React. This latter, in particular, I have been using for the past three years.
As time went on, the code we wrote was getting more and more different from the one served to the user. Nowadays, we’re transpiling our code in many different ways (using Babel and TypeScript, for example). We write ES2015+ and JSX, but the output code will be just HTML and JavaScript.
Currently, even though we can still use W3C tools to validate our web sites, they don't help us much with the code we write. We're still pursuing best practices to make our code more consistent and maintainable. And, if you're reading this article, I guess you're also looking for the same thing.
And I have something for you.
The Single Element Pattern (Singel)
I don't know exactly how many components I've written so far. But, if I put Polymer, Angular and React together, I can safely say that this number is over a thousand.
Besides company projects, I maintain a React boilerplate with more than 40 example components. Also, I'm working with Raphael Thomazella, who also contributed to this idea, on a UI toolkit with dozens more of them.
Many developers have the misconception that, if they start a project with the perfect file structure, they'll have no problems. The reality, though, is that it doesn't matter how consistent your file structure is. If your components don't follow well-defined rules, this will eventually make your project hard to maintain.
After creating and maintaining so many components, I can identify some characteristics that made them more consistent and reliable and, therefore, more enjoyable to use. The more a component resembled an HTML element, the more reliable it became.
There's nothing more reliable than a <div>.
When using a component, you'll ask yourself one or more of these questions:
Question #1: What if I need to pass props to nested elements?
Question #2: Will this break the app for some reason?
Question #3: What if I want to pass id or another HTML attribute?
Question #4: Can I style it passing className or style props?
Question #5: What about event handlers?
Reliability means, in this context, not needing to open the file and look at the code to understand how it works. If you're dealing with a <div>, for example, you'll know the answers right away:
Rule #1: Render only one element
Rule #2: Never break the app
Rule #3: Render all HTML attributes passed as props
Rule #4: Always merge the styles passed as props
Rule #5: Add all the event handlers passed as props
This is the group of rules that we call Singel.
Refactor-driven development
Make it work, then make it better.
Of course, it's not possible to have all of your components following Singel. At some point — in fact, at many points — you'll have to break at least the first rule.
The components that should follow these rules are the most important part of your application: atoms, primitives, building blocks, elements or whatever you call your foundation components. In this article, I'm going to call them single elements.
Some of them are easy to abstract right away: Button, Image, Input. That is, those components that have a direct relationship with HTML elements. In some other cases, you'll only identify them when you start having to duplicate code. And that's fine.
Often, whenever you need to change some component, add a new feature, or fix a bug, you'll see — or start writing — duplicated styling and behavior. That's the signal to abstract it into a new single element.
The higher the percentage of single elements in your application compared to other components, the more consistent and easier to maintain it will be.
Put them into a separate folder — elements, atoms, primitives — so, whenever you import some component from it, you'll be sure about the rules it follows.
A practical example
In this article I’m focussing on React. The same rules can be applied to any component-based library out there.
That said, consider that we have a Card component. It's composed of Card.js and Card.css, where we have styles for .card, .top-bar, .avatar, and other class selectors.
At some point, we have to put the avatar in another part of the application. Instead of duplicating HTML and CSS, we're going to create a new single element Avatar so we can reuse it.
Rule #1: Render only one element
It's composed by Avatar.js and Avatar.css, which has the .avatar style we extracted from Card.css. This renders just an <img>:
This is how we would use it inside Card and other parts of the application:
<Avatar profile={profile} />
Rule #2: Never break the app
An <img> doesn't break the app if you don't pass a src attribute, even though that's a required one. Our component, however, will break the whole app if we don't pass profile.
React 16 provides a new lifecycle method called componentDidCatch, which can be used to gracefully handle errors inside components. Even though it's a good practice to implement error boundaries within your app, it may mask bugs inside our single element.
We must make sure that Avatar is reliable by itself, and assume that even required props may not be provided by a parent component. In this case, besides checking whether profile exists before using it, we should use Flow, TypeScript, or PropTypes to warn about it:
Now we can render <Avatar /> with no props and see on the console what it expects to receive:
Often, we ignore those warnings and let our console accumulate several of them. This makes PropTypes useless, since we'll likely never notice new warnings when they show up. So, make sure to always solve the warnings before they multiply.
Rule #3: Render all HTML attributes passed as props
So far, our single element was using a custom prop called profile. We should avoid using custom props, especially when they're mapped directly to HTML attributes. Learn more about it below, in Suggestion #1: Avoid adding custom props.
We can easily accept all HTML attributes in our single elements by just passing all props down to the underlying element. We can solve the problem with custom props by expecting the respective HTML attributes instead:
Now Avatar looks more like an HTML element:
<Avatar src={profile.photoUrl} alt={profile.photoAlt} />
This rule also includes rendering children when, of course, the underlying HTML element accepts it.
Rule #4: Always merge the styles passed as props
Somewhere in your application, you'll want the single element to have a slightly different style. You should be able to customize it whether by using className or style props.
The internal style of a single element is equivalent to the style that browsers apply to native HTML elements. That being said, our Avatar, when receiving a className prop, shouldn't replace the internal one — but append it.
If we applied an internal style prop to Avatar, it could be easily solved by using object spread:
Now we can reliably apply new styles to our single element:
<Avatar
className="my-avatar"
style={{ borderWidth: 1 }}
/>
If you find yourself having to duplicate the new styles, don't hesitate to create another single element composing Avatar. It's fine — and often necessary — to create a single element that renders another single element.
Rule #5: Add all the event handlers passed as props
Since we're passing all props down, our single element is already prepared to receive any event handler. However, if we already have that event handler applied internally, what should we do?
In this case, we have two options: we can replace the internal handler with the prop altogether, or call both. That's up to you. Just make sure to always apply the event handler coming from the prop.
Suggestions
Suggestion #1: Avoid adding custom props
When creating single elements — especially when developing new features in your application — you'll be tempted to add custom props in order to configure them in different ways.
Using Avatar as an example, by some eccentricity of the designer suppose you have some places where the avatar should be squared, and others where it should be rounded. You might think that it's a good idea to add a rounded prop to Avatar.
Unless you're creating a well-documented open source library, resist that. Besides introducing the need of documentation, it's not scalable and will lead to unmaintainable code. Always try to create a new single element — such as AvatarRounded — which renders Avatar and modifies it, rather than adding a custom prop.
If you keep using unique and descriptive names and building reliable components, you may have hundreds of them. It'll still be highly maintainable. Your documentation will be the names of the components.
Suggestion #2: Receive the underlying HTML element as a prop
Not every custom prop is evil. Often you'll want to change the underlying HTML element rendered by a single element. And adding a custom prop is the only way to achieve that.
A common … [more]
Sent  from  my  iPhone  from iphone
20 days ago
« earlier      
accessibility active-admin advice agency ai algorithms amazon animation anti-patterns apis apple architecture art automation babel bernie bezier-curves big-tech bitcoin blog-idea blogging borders bundlers business c campaign capitalism cfp chicago china chromatic chrome class climate-change colors compilers components conference consulting cops criminal-justice css css-in-js culture cypress databases democracy democrats design design-patterns design-system designer devtools discrimination diversity dod domestic-abuse economics education elections elixir energy ethics exploitation facial-recognition fascism federal-jobs-guarantee feminism firebase font frameworkless frameworks free freelance from frontend fullstack gatsby gender gender-diversity gentrification gig-economy git global google government graphql heroku hiring humanism ice icons ide images inclusion income-inequality internationalism internet interpreter iphone javascript jest jquery jsx labor layout lisp maleness marx masculinity merge-sort metrics microsoft midwest militarism military moderation my nationalism ocaml open-source organization organizing pattern patterns pay-gap pentagon performance philosophy photos policing policy politics populism portals posgresql posters postgres prepack privacy processing productivity professionals programming programming-languages progressivism promises public-good race racism rails react react-native react-native-dom read reasonml reasonreact redux refactoring regex renders repo responsive ruby rural salesforce scaling sent seo serverless side-projects silicon-prairie silicon-valley socialism socket-programming sockets software speaking startup startups state state-management storybook storybookjs storycarding students styleguide styles surveillance svg talk talk-prep talks tdd tech tech-conference tech-workers technical-writing testing think-tank til tool trump tutorial tutorials typescript typography ui union unions ux video visualization vue wage-gap web-assembly webpack wellness white-collar wireframes work work-life-balance work-week workers working-class writing yahoo-pipes youtube

Copy this bookmark:



description:


tags: