React: Good Props/Bad Props
Have you ever written components with props names such as “isFirstElement”, “fromAuthenticationScreen”, “v2”, “adminVersion”? I did, and I regretted it months later when I had to add a new functionality ❌
In presentational components, those names can really be considered as huge warning/code smell, and this is what this article is about 💻
TL;DR
There are two kinds of props in React: good props and bad props
Your props should describe your component capabilities, not your component context
The Good Props/Bad Props rule
At the beginning: The Open-closed Principle
Whether you follow or not the Container/Presentational dichotomy (https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) or if you have your business logic encapsulated in hooks, you might still have presentational components in your codebase.
Ideally, those components must be reusable, in the sense that if you want to extend them to do something very similar, you should be able to do it easily, without modifying them.
This is what is called the Open-Closed principle (https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle), at least in its functional version.
Choosing good props
Being able to extend a component without modifying it has a lot to do with choosing the right props in the beginning, as we will see in our example.
I always struggle when I create the first version of a component. So I started to follow a rule of thumb:
Props should describe what a component does, not what a component is
And it works!
Example
Let’s take the example of a to-do list app. This app shows a list of items, where the first item is colored in grey, and there is a divider between all the items. Below is a good version and a bad version of the Item component:
The bad version — isFirstItem
The good version — showDivider
At first, the “isFirstItem” approach looks a bit better: it’s shorter and it enforces a consistent style across our different lists. But it has drawbacks :
- if we want to reuse our component somewhere else, it can be a bit tricky (1)
- we coupled the color and the divider of the item (2)
To illustrate 1 and 2, let’s imagine that we are very proud of our Item component and we want to reuse it to show an item that we selected somewhere else on our app, with grey color and no divider. The calls would look like this :
Even worse, let’s imagine that we want to have a white background, but no divider (or just have explicit props). We will have to rewrite our BadItemComponent, for example as such:
Ugly! We can now call our Component…
Such a refactor can easily create bugs (especially for more complex components) and as you increase the number of cases, you decrease the readability and maintainability of your component.
One of the problems here came from the fact that we encapsulated “business choices” (“make the first element to be grey”) and render logic. The other problem is that we cannot remediate this problem without updating the component. We broke both the separation of concerns and the open-closed principles.
The best of both worlds: The adapter
If we want to decouple our code, we can split presentation and convention. The goal here is to encapsulate the convention of your design systems in one component and the render logic in another component. It might be useful if you use your item component in many different lists and you want to keep your style consistent across all the lists of your website. We want to take the best of both worlds: consistent style but reusable components 🙌
An adapter would look like this:
In this adapter, we set up that we want the first row of our list to be grey, and that there is no divider for the first element.
If tomorrow, we want our list to be stripped (1 element out of 2 is grey), we have only one line to update:
Where does this leave us?
There are thousands of things to check when you create a new component. Giving a proper API is complex (naming variables, giving flexibility, etc.).
But following simple rules, such as the Good Props/Bad Props rule, can really help 👑
Clap if you apply this rule :)
To read elsewhere on the internet :
A good introduction by Kent C. Dodds on Inversion of control, and how to write better (=reusable) functions and components