DEV Community

Cover image for The 10 Component Commandments

The 10 Component Commandments

selbekk on June 22, 2019

Written in collaboration with Caroline Odden. Based on the talk with the same name and people, held at the ReactJS Oslo Meetup in June 2019. Cre...
Collapse
 
dance2die profile image
Sung M. Kim

Thank you @selbekk for the thorough "10 component commandments".

What are some of your favorite tips for creating cool components?

I am not sure if this is a tip or a bad practice, but I've started seeing a lot of code with following structure in "return/render" methods.

{isError && <ErrorMessage error={error}}
{isLoading && <Loading />}
{!isLoading && (
  <OtherComponentRequringData data={data} />
)}

Would such a way of showing components a bad practice?
I found it more declarative then having "if/else" (or using a ternary operator).

But the downside I found is that, many people aren't familiar with how to interpret the "state && component".

What would you think?

Collapse
 
ianesparrago profile image
Ean Esparrago

It's the short-circuit operator. I think it's neat.

background-color: ${p =>
      (p.attendanceStatus === "in" && p.theme.color.primary.main) ||
      (p.attendanceStatus === "out" && p.theme.color.out) ||
      (p.attendanceStatus === "later" && p.theme.color.warning) ||
      (p.attendanceStatus === "late" && p.theme.color.error) ||
      (p.attendanceStatus === "absent" && p.theme.color.absent) ||
      (p.attendanceStatus === "off" && p.theme.color.grey.medium)};
Collapse
 
ambroseus profile image
Eugene Samonenko • Edited

also found useful this pattern as more declarative, but need to use it carefully. my early pitfall was to use array.length && ... instead of array.length === 0 && ...

Collapse
 
andrii2019 profile image
andrii2019

What about just using
!!array.length && ...?
It looks smarter and shortly.

Thread Thread
 
ambroseus profile image
Eugene Samonenko

yep, it's shorter :) use !! many years while coding on perl. now I prefer Boolean(array.length)

Collapse
 
thewix profile image
TheWix

This is great advice. Falsey and Thruthy values bit me in the same way. I do things like isEmpty(array) or if I am really defensive isNullOrEmpty(array)

Collapse
 
andrii2019 profile image
andrii2019

What about just using !!array.length && ... ? It looks smarter and short.

Collapse
 
edwincarbajal profile image
Edwin Carbajal

I had the same question 🤔

Collapse
 
seanmclem profile image
Seanmclem

Does eslint still flag it?

Collapse
 
ambroseus profile image
Eugene Samonenko • Edited

my favorite tip with redux connect & compose to reduce boilerplate (with HOCs):

const mapStateToProps = state => ({ ... });

export default compose(
  withRouter,
  withStyles,
  ...
  connect(mapStateToProps)
)(MyComponent);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
pinkbunny1 profile image
Jin Lee

Could you explain how it works though ? if you have your above code as a ComponentA, and lets say I call the component with some props like so. What happens inside of it ?

Thanks in advance

Collapse
 
ambroseus profile image
Eugene Samonenko
Collapse
 
falldowngoboone profile image
Ryan Boone • Edited

One of the rules I try to follow is don’t make something a shared component until I’ve copied and pasted it four times. Too often I would create components too early, without any context beyond the current one. This would lead to components with awkward and bloated APIs from other developers trying to adapt it to their needs.

Also, if your component encapsulates complicated code structure, offer a way for developers to get to the individual components. For example, say you’ve created an Input component that renders a label and control, as well as an error message on invalid input. Give developers a way to access the individual components and allow them to completely recompose (or add to) the component’s structure if they have to.

Some library APIs give you render props for each component, but that quickly gets messy when you have to add a labelRender, a controlRender, and an errorRender. I personally like compound components exported from the original component (e.g. <Input.Label>), along with React Context providing the necessary linking state (e.g. the input’s ID for the label’s htmlFor prop).

Collapse
 
allpro profile image
Kevin Dalman

I... don’t make something a shared component until I’ve copied and pasted it four times... This would lead to components with awkward and bloated APIs from other developers trying to adapt it to their needs.

The problems with this are:

  1. Your duplicated code likely has minor updates in each iteration, instead of refininements to a 'common component'. Now it is more difficult to unify them.
  2. If you needed the same functionality 4 times, it's likely other team devs did as well. Since there was no common component, each had to reinvent the wheel, each time with different API's. Now it's very difficult to unify all these components to use the same common one, along with all their tests. This is so much work that it usually is never done, and the duplicated code remains, complicating future enhancements and maintenance.

I find it best to create common components as early as possible, then encourage reuse rather that duplication, (DRY). Common components should evolve as new needs arise; this is agile coding! Even heavily used libraries like MaterialUI are updated regularly, so your own 'library' of components should be as well.

Good code reviews can prevent overcomplicating common components. Guidelines like the ones in this article, along with app standards, also help keep API's simple and consistent across an app. It's easier to refactor a common component occasionally than to replace totally different implementations of the same functionality.

Collapse
 
falldowngoboone profile image
Ryan Boone

I agree that good code reviews can prevent over complicating common components, but I’m not as concerned with reinventing the wheel, at least not at first. I believe components need to earn their right to exist, and by the time they have earned this right, you typically have a good starting point for an API, and a good reviewer can pick this out.

Also, in my experience, if I am having difficulty unifying components into a common component, there’s a good chance they aren’t as common as I might have originally thought. I may be better served in extracting out common related logic into a helper function or hook.

With the projects I work on at work, I tend to fall on the side of making code as disposable as possible, so take what I’m with a grain of salt. I am very hard on what makes its way into our shared common components.

Honestly, whatever works for you is great, especially since this is one of those areas where it depends a lot on the context of the work.

Collapse
 
pedromass profile image
Pedro Mass

For Tip 2. Allow for contextual semantics

function Grid({ as: Element, ...props }) {
  return <Element className="grid" {...props} />
}
Grid.defaultProps = {
  as: 'div',
};

What would the propType be?

Something like this?

Grid.propTypes = {
  as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType])
};
Collapse
 
asherccohen profile image
Asher Cohen

This was the one I preferred. But in a non-typescript world that would require a lot of conditions to return the right element type.

Am I right?

Collapse
 
selbekk profile image
selbekk

Good question! It’s not a very easy type to get right, especially if you want the correct props of the as element to be applied as well. It is doable though, with a drizzle of keyof and some other ts ninja tricks. I’m not a great TS dev yet, and I’m on my phone atm.

There is lots of prior art here - look at reach UI or styled components’types for possible impoementations

Collapse
 
selbekk profile image
selbekk

Yeah that would be it!

Collapse
 
ganevdev profile image
Ivan Ganev • Edited

Great article, thanks.

As I understand it, this is a small typo (tooltipp)?

className = {classNames ('tooltipp', props.className)}

I especially liked the eighth recommendation - about HTML attributes. Does anyone know if there is an eslint rule for this (for react)? And is it even possible to make such eslint rule? At first glance, this is possible.

Collapse
 
selbekk profile image
selbekk

Hi Ivan! Yeah, thanks, I’ll correct it.

I’m not sure about an eslint rule, bur it should be doable I guess :-)

Collapse
 
maciekgrzybek profile image
Maciek Grzybek

Great article mate 👌

Collapse
 
selbekk profile image
selbekk

That’s a nice way to do it, too. I tend to use prop types or type annotations to make that assertion for me, which reads better to me.

Collapse
 
michaelaubry profile image
Michael Aubry

This is great!

Collapse
 
palnes profile image
Pål Nes

Just a heads-up that Storybook will get an MDX documentation add-on in the next version. We're running the technical preview at our end. :)

Collapse
 
trondeh80 profile image
DevTron

Awesome post! Inspiring work dude!