This article will show you how you can create more reusable and readable components in React by sticking to just a few simple rules.
I believe that the best way to learn is by example, so we’re gonna rely on a simple application that displays cars from API in a different way.
Application available here:
https://github.com/kwdowik/reusable-components
Ok, let’s start cooking!
In order to prepare a delicious dish we have to stick to the recipe, which includes:
- Reusable logic
- Reusable presentation
- Separate concerns (logic and presentation)
- Extensibility
Our starting point will be CarsList
component onmain
branch from this reusable-components repository.
It’s nothing fancy. The CarList
component just fetches cars from the API and then renders them as a list element. We have everything in one file, logic and presentation, and even for this file which is very simple, we can quickly hit the wall and start to duplicate the code once we gonna start adding new components.
Now in order to add all of our ingredients, we will have to follow /src/components/doc.md
file where we can find what we have to implement and how we can do that, so let’s begin!
Reusable logic
Our task will be to make use of our logic responsible for fetching cars in different places and still following the DRY (Don’t Repeat Yourself) rule. If you’re following the code please checkout to ex1/shared-logic branch.
First, we have to extract our logic to separated custom hook, let’s name it useFetchCars:
..then just import that hook inside CarList
component:
Thanks to that we make our fetching cars logic, component agnostic and we can reuse it in any other place. Let’s add new CarsTable
component:
..and we finished our first task!
Reusable presentation
Please check out to ex2/shared-presentation branch.
Now it’s time for our second ingredient. We’d like to reuse our presentation component CarsList
which right now is tightly coupled with data from useFetchCars
hook, that’s not good.
Assume that we’d like to display a list of cars in exactly the same way but having data from a different source (eg. graphql), so let’s pass cars as a prop to the CarsList
component:
..and we have already achieved separation of concerns, but now we have to connect our new CarsList
component with data in order to keep your previous functionality unchanged. Add CarsListContainer
container that will connect presentation with logic and rename CarsList
component to CarsListPresentation
:
CarsListPresentation
the component doesn’t know anything about the source of data that makes our presentation reusable. Great, we’ve done the next task!
Extensibility
Please check out to ex3/extensibility branch.
The last task will be to make our CarsListContainer
extensible, but why we need to do that?
With current implementation if we’d have to add new logic to the container we will have to change our container, I wouldn’t like to do that in order to not break the Open/Close principle which says “software entities should be open for extension, but closed for modification”, so if any new behavior would have to be added to CarsListConatiner
I’d rather create a new code, responsible only for that behavior according to the Single Responsibility principle, and then use composition. We can achieve that using pattern that’s being used in the react for a while Higher Order Components.
Our HOC will get CarsPresentation
the component as an argument and pass cars as a prop.
in the end, we invoke withFetchCarsHOC
with CarsListPresentation
component and we are done!
If we would like to create a new HOC with different behavior we can use compose
method to connect it with others
Bon appetite
Let’s check if we’ve added all the necessary ingredients:
- Reusable logic, we made our logic reusable thanks to extracting it to
useFetchCars
hook. - Reusable presentation,
CarsListPresentation
get a data as a prop which makes our component is logic agnostic and reusable. - Separate concerns (logic and presentation), our logic and presentation are separated because we have
CarsListPresentation
which doesn’t know anything about the data source, anduseFetchCars
hook that’s not aware of the presentation. - Extensibility, after
withFetchCarsHOC
has been created we can add new behavior toCarsList
in a composite way without changing existing implementation according to Open/Close principle.
Well, it seems that we have added all the necessary ingredients which make our application a better place for future improvements.
Twitter: k_wdowik