HomeAboutRelease Notes

Some Good Practices about Developing Shared UI Components

March 11, 2021

To boost development efficiency in work, sometimes we need to develop shared components. In such case, how we design the shared components are vital to how efficiently codes can be reused. A well designed shared component usually is easy to use and extend, while a poorly designed shared component not only may be inefficient, but also sometimes causes backfires (Then nobody wants to reuse it T-T).

In this blog post, I would like to share with you my tips about developing shared components, which is the experience I gained from maintaining a company wide used component library and form builder library in my previous work.

1. Do documentation and demonstration of components

Documentation is the best guide for your users to get started using your shared components. If you have one short paragraph to summarize what problem your component solves, your users will be confident to start trying your shared component and get to know how they can use these components in their projects.

After the brief introduction, you can then provide some more detailed information about the components, such as how to install them (npm install xxx or yarn add xxx), how to use them (use cases by examples!), what the API specification is (property names, data types, description and example values).

Since we are the developers doing UI and words are plain, it’s even better if you can demonstrate your components on a static website, so that your users can have a good idea about how your component looks like and what is capable of before considering using it.

One of the best demonstration tool I have ever used is Storybook. You can easily configure a static web page to show your component examples with it.

Video extracted from Storybook

2. Don’t miss unit tests

It has become a common sense for software engineers to write unit tests for shared components. It’s vital to maintain the stability of your components as you may have many users to consume your shared components.

When you are upgrading your shared components, you don’t want to accidentally break something and cause disastrous outcomes to your users. So every time when you make any changes, you should do regression tests to your components.

And manual regression testing is redundant and inefficient, so that you should consider to write unit tests for your shared components and try to mock all kinds of scenarios to ensure your components working fine under all situations. With unit tests, you only need one command to trigger automated testing and wait just for seconds to see the results.

sample-unit-test

Sample unit test result

There are a bunch of awesome testing frameworks to use such as Jest and Mocha.

Besides, checkout another one of my blog posts 6 Tips for Beginners to Write Good Unit Tests

3. Consider Extensibility

Often, you need to upgrade your shared components to adapt daily growing requirements. Hence, extensibility is quite important. You have to consider to leave some entry points for future improvement.

A good way to handle it is to use Plugin design pattern. In short, you would deliberately create some hook functions in your shared components for you or even your users to add more customized / upgraded functionalities.

There is a great example which practices nice extensibility very well, dayjs.

Below is the official example provided by dayjs. advancedFormat is a custom plugin and dayjs can extend it by just calling extend() function.

import advancedFormat from 'dayjs/plugin/advancedFormat' // load on demand dayjs.extend(advancedFormat) // use plugin dayjs().format('Q Do k kk X x') // more available formats

We can take a look at how advancedFormat() is created in dayjs.

// dayjs/src/plugin/advancedFormat/index.js export default (o, c, d) => { // locale needed later const proto = c.prototype const oldFormat = proto.format d.en.ordinal = (number) => { const s = ['th', 'st', 'nd', 'rd'] const v = number % 100 return `[${number}${(s[(v - 20) % 10] || s[v] || s[0])}]` } // extend en locale here proto.format = function (formatStr) { const locale = this.$locale() const utils = this.$utils() const str = formatStr || FORMAT_DEFAULT const result = str.replace(/\[([^\]]+)]|Q|wo|ww|w|WW|W|zzz|z|gggg|GGGG|Do|X|x|k{1,2}|S/g, (match) => { switch (match) { // Hide the implementation. Checkout dayjs project for details } }) return oldFormat.bind(this)(result) } }

Basically, advancedFormat() is a function which has 3 parameters o, c, d, which stand for option, dayjsClass, dayjsFactory (reference: Plugin · Day.js).

Let’s take a look at how the extend() function works.

// dayjs/src/index.js dayjs.extend = (plugin, option) => { if (!plugin.$i) { // install plugin only once plugin(option, Dayjs, dayjs) plugin.$i = true } return dayjs }

Apparently, the function of these 3 parameters is the “hook”. The parameters are the objects passed from the dayjs core module, which can be manipulated freely within your custom plugin, so that the Dayjs class can have all the powerful custom functionalities created by you.

4. Make Your API Simple and Concise

What is the nightmare of using a shred component to most users?

Too many configurations required!

Imagine a component which requires you to put 10+ or even 20+ parameters to make it work properly. Would you be really frustrated to get started using it?

<PseudoComponent attributeA={...} attributeB={...} attributeC={...} attributeD={...} attributeE={...} attributeF={...} attributeG={...} attributeH={...} attributeI={...} attributeJ={...} attributeK={...} attributeL={...} attributeM={...} attributeN={...} />

A better way is to only make necessary parameters compulsory and the rest can be optional, besides, provide a detailed documentation which explains what the attribute is used for, what types of value it accepts and examples of values.

A very good example would be Ant Design’s component documentation.

ant-design-doc-example

Sample API documentation from Radio - Ant Design

However, if you really have a long list of mandatory attributes required for your shared component, please make sure your documentation is detailed and provide as many examples as possible to help your users understand what should be done and how. As the developer, of course you are 99% familiar about the intention of every attributes, but most of your users would be clueless about them if there is not much explanation and not enough examples to demonstrate!

Summary

So these are the tips I would like to share with you today. Actually, these tips are just basic ones. There are many other practices which contributes in building good shared components. I hope you could share your thoughts and good practices in the comments so that we all can learn together! Cheers~

Featured image is credited to Free Creative Stuff from Pexels


Written by Yi Zhiyue
A Software Engineer · 山不在高,有仙则灵
LinkedIn · GitHub · Email