Blogged Answers: React Components, Reusability, and Abstraction

This is a post in the Blogged Answers series.

Thoughts on the tradeoffs of trying to write "reusable components"

I just replied to a question by a developer who was concerned that they were writing components that aren't "reusable", and wanted to know how important that is, and figured it's worth pasting that here for reference.

Reusability is great, but it's also not necessarily a primary goal. As I've grown as a software developer, I've learned that it's always easier to make something hardcoded and specific, and harder to make it generic / abstract / reusable. Genericizing code means that you have to think through all the possible ways that this might get used in the future, and handle all those possible use cases.

As a specific example here, see the new createAsyncThunk API we recently added to Redux Toolkit. Lenz Weber, Matt Sutkowski, and I spent weeks thinking through ways to deal with error handling, API parameters, and return values. I think we came up with something that works pretty well, but it took a lot of effort.

As programmers, we do get focused on "oh, these two pieces of code look almost the same, we can turn this into one piece of code and use it in both places!" I used to be really bad about this in code reviews - if I saw the same 3-line snippet in a couple places, I'd ask the PR author to extract it into a function or something.

What finally snapped me out of that was a post by Sandi Metz entitled The Wrong Abstraction, where she talks about how trying to abstract or genericize code too early can often lead to making bad abstractions, and that it's often better to let code be duplicated for now rather than try to abstract it right away.

This applies to any code in general, as well as React components. Specifically for React, I've found that my components generally fall into one of three categories:

  • Truly generic components that could theoretically be used across all projects, like a <Button>. This is especially true if you're in a larger team that has some kind of a design system.
  • Components that may be more specific to an app, but still have some potential for reuse
  • App-specific components that are very tied to the structure and logic of this one app

Overall, the smaller and more "primitive" a component is, the better a candidate it is for reuse.

Don't get me wrong, reusability is a good thing. But, I do think that trying to focus on reusability as the primary goal is misleading. The primary goal is to have code that runs, accomplishes the desired functionality for the feature and the app, and does so without bugs.

I've always been a huge fan of the mantra "make it work, make it right, make it fast". Focus on getting something working, even if the code is ugly and/or duplicated. Then you can improve it.

I wouldn't worry about trying to make components "reusable outside this project" for now. Make something that works. If that means copy-pasting some code, do so. Once you've got this working, then take some time to evaluate what you've got, look for duplication and patterns, and extract something reusable, if it even makes sense to do so.

This is a post in the Blogged Answers series. Other posts in this series: