Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
There are so many best practices and advanced patternsâââthe problem is keeping track of them and keeping them in mind as you develop new components.
I wrote this checklist to help myself remember all of the things I need to be thinking about as I go through building highly reusable components.
This checklist applies equally to both Vue and React.
Really, it doesnât matter which framework you use, since the principles of component composition are exactly the same. And the general process for designing a component doesnât change either.
Condensed checklist at the end of the post.
Avoid speculative generality
This one sounds pretty obvious, but I find myself forgetting this all the time.
You need to fully understand your use case, and only implement that. If you just dive into code without thinking first, youâll end up writing in a bunch of features that will never be used. Thatâs a colossal waste of your precious time.
body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
Over the past couple years I've gotten much more sensitive to the cost of speculative generality. So many bugs could have been avoided by just solving the problem at hand instead of trying to solve a ton of potential future problems.
âââ@jaredforsyth
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}
When writing a component that is supposed to be reused across many different use cases, the temptation to provide more and more abstraction is easier to justify.
However, if you write the component well, it should be fairly easy to come back later and add in new use cases you couldnât anticipate.
This is where 80/20 thinking comes in handy. A few of the features you implement will be responsible for handling most of the use cases. Focus on those.
Iâll leave you with one last tweet about APIÂ design:
body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
Solved problems need constrained APIs. Unsolved problems need unconstrained APIs." Paraphrasing @cramforce. This really resonates with how I think about framework design. With time you learn a bit more about the right way of doing things, but constraining too early is fatal.
âââ@sebmarkbage
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}
Simplify the API
Now that weâve stopped to think, letâs simplify our API as much as possible.
Youâre also trying to find the sweet spot between simplicity and flexibility. As Ken Wheeler said in A Bitter Guide to Open Source:
You want to find the perfect balance of working out of the box, and configuring as necessary. On top of that, you want to keep things clear, explicit and approachable. Donât be too clever or you will piss everyone off.
Too simple and it doesnât cover enough use cases to be helpful. Too flexible and it becomes too difficult to understand how to do anything.
Just take a look at how many options this jQuery Datepicker has. Itâs terrifying!
This is where more advanced composition patterns really come in handy.
Typically youâre making trade offs between simplicity and flexibility. But techniques like render props and compound components allow you to achieve more flexibility while reducing complexity.
Research prior art
We have a pretty good idea of what we weâre building now, but first we need to see what else is out there.
There are a few reasons for this:
First, we can get great ideas for our implementation.
If we look at mature open source projects, they will be more or less feature complete. This lets us double-check our assumptions about what we might need to build out.
We can also see how the API was built, what abstractions were used, and possibly even some implementation details we wouldnât have considered.
Second, we can potentially save ourselves some work.
Thereâs a good chance weâll find something that we can use to speed up development. We may even find something that ticks all our needs, and end up short-circuiting this whole process.
In his Advanced React.js course, Ryan Florence mentions that he likes to check out jQuery plugins. Theyâve stood the test of time, and likely have all the features that youâd ever consider putting in a UIÂ element.
A great place to start is in the Awesome repos:
Break up components into bite-sized pieces
Compositional frameworks use components as their main technique of abstraction (vs. objects in OO or functions in functional programming).
If you shove everything into one or two giant components, you arenât taking advantage of this at all!
Smaller components are better:
- Better abstraction makes your code cleaner, easier to understand, and easier to maintain
- Encourages reusability of the code
- Necessary in order to use more advanced fancy-pants composition techniques
There are a couple main ways to think about splitting up components:
Isolating behaviour from presentation
You have state and logic in one component, with a separate component responsible for rendering. We would typically do this through render props and stateless functional components.
Levels of abstraction
Itâs often a good idea to keep the level of abstraction consistent within a component. Either the component would deal with native DOM elements, like div, p, span, or it would deal with framework components.
Just be careful, because you donât want to go overboard and create too many components! Kent C. Dodds has a great article on how to know when things should be split up.
Use powerful composition patternsâââonly where needed
Render props, provider pattern, higher order components. These patterns are seductive and fun to use.
But they are not always the right tool for the job. Knowing when to use each one to itâs maximum effect is important. Otherwise you may use the wrong tool for the job and dig yourself into a hole.
If you want to learn more about these, I highly suggest checking out Kent C. Doddâs article on advanced patterns in React:
Compound components
Break up parts of your component into smaller bits that can be recombined in different ways.
For example, take your Table component and break it down into TableHeader, TableBody, and TableFooter components. Most people will be fine to use the regular Table, but now someone can build their own table and build a custom header for it without too much trouble.
Higher order components
Take a component and wrap it up inside another component to add functionality to it. This concept evolved from the object-oriented idea of mixins, but has largely replaced it (although Vue still has supports mixins).
When React first came on the scene, mixins gave developers an escape hatch, allowing them to go back to something familiar from the object-oriented world.
After awhile we figured out that composition is far better than inheritance, so higher order components became the norm.
Render props and renderless components
Now weâre seeing another shift. This time away from higher order components, and towards what are called render props (or function as children).
Render props let us delegate rendering to the parent component, giving us even cleaner separation between behaviour and presentation. If you combine this pattern with the prop getters and setters pattern, you can build incredibly flexible and expressive components with very few lines of code.
Controlled and uncontrolled props
Does the component control the state, or does itâs parent control the state? That is the difference between an uncontrolled and a controlled prop.
Generally, uncontrolled props are used since they allow us to encapsulate complexity and state within the component. However, if you want more flexibilityâââwhich we need when writing a highly reusable componentâââyou can allow a prop to be controlled by the parent.
State reducer
This is a pattern I learned from Kent C. Dodds in his project Downshift (more explanation here).
If youâre going to expose a prop to be controlled, why not expose the entire state reducer? Then you can have extremely fine-grained control over the behaviour of the componentâââbut you donât have to re-write any logic.
Brilliant.
Provider pattern
If youâve used Redux or VueX or something similar, you probably know this pattern.
Instead of passing props around all day long, and passing them down through many many levels of components, you simply âinjectâ them where they need to be.
In React this can be done using context, or provide and inject in Vue.
Accessibility is not an afterthought
Iâm not very good at accessibility yet.
Itâs something Iâve been getting better at recently though, because I believe itâs incredibly important to making the web a better place.
Since Iâm not an a11y expert, Iâll instead get you to read this article Addy Osmani wrote on developing accessible web components. You can also check out the great resources of The A11YÂ Project.
Accessible UI Components For The Web
Make it easy to style
How easy is it for consumers to style your component the way they need?
The intention is that your component will be used all over the place. In all sorts of wild and various contexts. It will be used in ways you could never have imagined even in your weirdest dreams.
You need to make sure that other devs can easily style and change the layout of your component without resorting to ugly CSS overrides.
Renderless components make this super simple. You can just delegate all rendering to whoever is using your component.
However, in many cases itâs useful to render something by default. A good approach here is to use BEM in your component and provide CSS classes that can be easily overriden.
But CSS isnât Turing complete!
Alternatively, you can take the cool-kid approach and use CSS-in-JS (itâs not for everyone).
When this idea started out, most libraries were implemented using inline styles. This caused some performance issues and had some other issues as well.
Nowadays, most libraries work by dynamically attaching stylesheets to the DOM, and the biggest issues have been resolved. I havenât had a chance to dive into these in awhile, but itâs on my list!
Thereâs tons of libraries, talks, and articles out there about this if you want to learn more, but here is a good place to start for React, and here for Vue.
Do you even test, bro?
You donât want tons of people using your beautiful new component, only to have it flake out on edge cases. Or heaven forbid you add a slick new featureâââbut it breaks everything else.
When youâre building a reusable component like this you probably wonât do any integration or end-to-end testing. Youâll be writing unit tests that focus on testing tricky logic and making sure the component is rendering properly.
Here are a few great articles on testing React and Vue components if you need to get started:
- Fullstack React: Introduction to Testing
- Unit Testing Vue.js Components with the Official Vue Testing Tools and Jest | Alex Jover Morales
DocumentationâââItâs what makes all the difference
âIâll write the docs laterâ, you think to yourself as you push the last beautiful crafted commit to Github. âI mean, even if I donât get to it no one will really care.â
After all, youâre a professional at writing in Javascript, not one of these ânaturalâ languages that humans speak.
But great documentation is what turns a good component into a great component.
You know the feeling when youâre trying to figure out how to get a library to do something, or you get confused about Reactâs (or Vueâs) lifecycle methods. If you canât find the answer quickly, itâs super frustrating.
Sometimes youâll even get rid of the library because you canât figure out how to get it to work!
Donât let your shiny, brand new component to go to waste because of a lack of good documentation.
Condensed Checklist
Now that weâve gone through all of that, here is the easy-to-reference version:
Avoid speculative generalitySimplify the APIResearch prior artBreak up components into bite-sized pieces
- Separate state + behaviour from presentation
- Split up by level of abstraction
Use powerful composition patternsâââonly where needed
- Compound components
- Higher order components
- Render props and renderless components
- Controlled/Uncontrolled props
- State reducer
- Provider pattern
Accessibility is not an afterthoughtMake it easy to styleDo you even test, bro?DocumentationâââItâs what makes all the difference
Follow me on Twitter if you want to read and learn more about Vue and React!
Checklist for Writing Highly Reusable Components in React and Vue was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.