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:
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)
Our starting point will be
CarsList component on
main 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!
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
..then just import that hook inside
Thanks to that we make our fetching cars logic, component agnostic and we can reuse it in any other place. Let’s add new
..and we finished our first task!
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
..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 the component doesn’t know anything about the source of data that makes our presentation reusable. Great, we’ve done the next task!
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
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
Let’s check if we’ve added all the necessary ingredients:
- Reusable logic, we made our logic reusable thanks to extracting it to
- Reusable presentation,
CarsListPresentationget 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
CarsListPresentationwhich doesn’t know anything about the data source, and
useFetchCarshook that’s not aware of the presentation.
- Extensibility, after
withFetchCarsHOChas been created we can add new behavior to
CarsListin 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.