One trick to make React Components more reusable

Llorenç Muntaner
Onedot
Published in
3 min readApr 17, 2018

What do you do when you have components that are “same same, but different” and you want to reuse them?

Let’s see an example

Upload Button

We have 2 very similar components:

They both are about uploading files. However, one button is used to create a Dictionary and the other one Data. The differences between Data and Dictionary are not relevant at the moment.

Both buttons have a similar behaviour. They open the file system to select files. When selecting them, the files are uploaded to our system.

However, there are a few differences.

  • They should create a different entity, which means they hit a different endpoint of our API.
  • The text of the button is different.
  • They accept different mime types.

We first created the UploadButton Component for Data. When we wanted to implement the UploadButton for Dictionaries we faced 3 possible patterns to reuse the component:

Option 1: Use a prop to set the type

// In our Data page
<UploadButton type="data" />
// In our Dictionary page
<UploadButton type="dictionary" />

This way the UploadButton is very easy to reuse. However, the implementation of the UploadButton is not very elegant. We have to check the type prop to use one or another thing.

Our UploadButton will grow with conditionals if we add functionality to it. It will also be very burdensome if we ever wanted to introduce a new entity apart from Data and Dictionary.

Option 2: Pass the differences as props

// in our Data page Component
<UploadButton
text="Upload data"
acceptedValues={ this.props.dataAcceptedValues }
handleUpload={ this.props.startDataUpload }
/>
// in our Dictionary page Component
<UploadButton
text="Upload dictionary"
acceptedValues={ this.props.dictionaryAcceptedValues }
handleUpload={ this.props.startDictionaryUpload }
/>

This looks good by itself. However, when you use it in a Component that also has some other Buttons and Components, the props of that component become a huge list. Making the component hard to read and understand.

Option 3: Create new Components

// in our Data page Component
<UploadDataButton />
// in our Dictionary page Component
<UploadDictionaryButton />

That would mean that we need two new components: UploadDataButton and UploadDictionaryButton.

// in UploadDataButton
<UploadButton
text="Upload data"
acceptedValues={ this.props.dataAcceptedValues }
handleUpload={ this.props.startDataUpload }
/>
// in UploadDictionaryButton
<UploadButton
text="Upload dictionary"
acceptedValues={ this.props.dictionaryAcceptedValues }
handleUpload={ this.props.startDictionaryUpload }
/>

By creating them as standalone components we made them easier to use and reuse. Just import it. No need to pass any prop. We can state this pattern with the following principle:

More components with less logic vs less components with more logic

If we ever need to add some new feature we can do that easily on the standalone component. If the feature that we need to add is common to both, we will have as well the UploadButton for it.

Conclusion

This pattern brought us a few extra features:

  • Easy to reuse components
  • Components with fewer props
  • Flexibility for future features

The downside is that incremented the number of components we use from 1 to 3. In our case it is a trade off we are willing to make.

This is a simple scenario to illustrate the pattern. We have applied it to more complex situations with success.

We love hearing back from the community, so don’t hesitate to leave a comment.

Thanks for reading!

Originally posted on Onedot Blog.

--

--