Composition and modularity with React
Tags: react
Published: 2019-09-10 12:49:00

 
This article is based on the compound pattern. It was presented for the first time during a talk by [Ryan Florence](https://ryanflorence.com/) at [ReactJS Phoenix 2016](https://www.youtube.com/watch?time_continue=1&v=hEGg-3pIHlE).
I discovered it with [Egghead.io](https://egghead.io/ "Egghead.io"), during a [course](https://egghead.io/courses/advanced-react-component-patterns "Kent C. Dodds") done by Kent.C.Dodds. You can find more resources on his [blog](https://kentcdodds.com/).
The pattern puts emphasis on modularity and components composition.

In this article we will use [React Context](https://reactjs.org/docs/context.html) and [React Hook api](https://en.reactjs.org/docs/hooks-reference.html).

tl;dr 
In my company I faced some issues building reusable shared components. I'm going to show you how to tackle these problems, enhance composition, and still give the user the possibility to customize the behavior.

## The monolith component

You are being asked to create a simple shared component, a DataList with a select all elements action.

{% gist https://gist.github.com/nicomaligne/1f0c0ccc03ffbe9a873e6a369fe0efe3 %}

So you did a simple chunk of code, nothing fancy. You have built a DataList component, with your action and the table with the data.

But soon more is asked, from different projects with more or less time to do it. And the simple shared DataList is becoming more and more complex.

{% gist 
https://gist.github.com/nicomaligne/d84e591c37fb6a08e9c89513a08303fd
%}

Each project has different needs, so everything must be conditional, meaning that this shared component has to hold all the projects' logic.  

This is what it looks like in your project

{% gist https://gist.github.com/nicomaligne/ef38e29b5a0df056598e2c1e851e633b %}

So here you have two actions (foo and bar), the selectAll and the table...
The api and the desired output are hard to read, it's the beginning of the nested props problem.
It's becoming a nightmare, and tweaking or adding features is now a really hard job.
Let's not speak about the documentation...

![Alt Text](https://media.giphy.com/media/RkSBVXwrISiPK/giphy.gif)

The Monolith has beaten you...

##  Compound pattern to the rescue

In his course KCD talked about what he called the compound pattern. The name is self speaking, it involves to break down our components into smaller ones to allow more flexibility.
Instead of being only props driven, we want to be component driven.

{% gist
https://gist.github.com/nicomaligne/15088ca30e141af910769cad79816941 %}

We have made every component accessible from outside and now you can compose them as you want. Our DataList could be seen as a dictionary of components to make YOUR Datalist.

{% gist https://gist.github.com/nicomaligne/80e67e3f4e5b3ca566180984c502898d %}


In the example above, you can see that the props api is way easier to read. There is no more nested props and each component gets is own set of params.

Let's move on and continue to improve it.


## Sharing props

We have some redundancy props. Let's fix that.
First we have to create a new component called Manager, it will hold our context provider for the List and wrap all our compound components.

{% gist https://gist.github.com/nicomaligne/9196e4b73e5375e5a836d3d186f1e83d %}

We can use the [context](https://reactjs.org/docs/context.html) to easily share  some props between our children components.
For example to simplify the api, we can decide to pass the id to our manager. 
Each child will access it from the consumer, and will create a unique id based on a common value.

{% gist https://gist.github.com/nicomaligne/3032763de39ebd1a74b60968cef0ea77 %}

So after the changes we have something like this

{% gist 
https://gist.github.com/nicomaligne/fefbaaea7339e8ae7b36f9325875276d 
%}

Our DataList is easy to import and manipulate. We have already made huge progress since our first list draft, but we are still missing some true component logic.
We want to implement a button with the ability to switch between two display modes, a column one and an expanded one.

## Sharing logic

We have to share the display value between two components, the one the user can trigger to change the value, and of course the list to display the correct render mode.
So our Manager component, which is the parent of everyone, seems to be the right choice.

We are using the [useReducer](https://reactjs.org/docs/hooks-reference.html#usereducer) here.
{% gist https://gist.github.com/nicomaligne/fb72e6b1971963fa52cd675ee1689d67 %}

Our manager will hold the state of the current display, and it will also provide the dispatcher.

In our DisplayMode component we can use the dispatch function passed through the context.

{% gist https://gist.github.com/nicomaligne/3ac87f6b8e2803deca265cb69b4c0f1f %}

The same applies to our Table, we can retrieve the value from the state store in the context.

{% gist https://gist.github.com/nicomaligne/f101032d6e360256874ca73511f4641a %}

So now we have our first business logic implemented.

{% gist https://gist.github.com/nicomaligne/ad2b6ab060c7dbbfaced1067ba93d3ef %}

But the user lacks control on the component.


## Allow business logic customization

So we want to empower our components with user logic, but also keep a simple api.

{% gist https://gist.github.com/nicomaligne/fec24932aa9f21c3feaa978e3eb981b0 %}

Now the Manager can take an initialDisplayMode and put it in the reducer's state as initialState.

{% gist 
https://gist.github.com/nicomaligne/3c46fb2aafe3b33adb53883bbc575fdd %}

This is one customisation, you can change the initial value but keep all the internal logic.

But we also want to go full control.

{% gist https://gist.github.com/nicomaligne/db85ceefc9b92c871706c201a9131563 %}

Now the component can take a callback and be controlled by the user.

We just have to allow the List to receive the value.

{% gist https://gist.github.com/nicomaligne/20a91ab45d2719b026027435682efe98 %}

## Conclusion

{% gist 
https://gist.github.com/nicomaligne/c85e460ff6a65fb5482d44b8a37527c0 %}

A simple api that users can play around with and compose depending on their need, given also the possibility to add their own logic where they need. You can mix fully controlled components with uncontrolled ones, if you want.

Compound pattern, context and hooks allow to create very flexible and customisable components.

Let me hear what pattern you have found to solve your day to day problems
and don't hesitate to leave comments and share your opinions.

Happy react ;) 

## Bonus

You can still improve some parts of the api and make it more opinionated.

Let's enhance our context api

{% gist 
https://gist.github.com/nicomaligne/f3a13b8c148212ea04781017320ac924 %} 

Now if someone tries to use a compound component outside its context, an error will be thrown.

We can also alert if the controlled mode is not used well.

{% gist https://gist.github.com/nicomaligne/8621c680c52eae7cce3d5e399a714a54 %}

And use it like this

{%gist https://gist.github.com/nicomaligne/5e63f06997f13459da6c167601c9fb66 %}

If the user forgets to pass a value and be fully controlled, it will throw an error. It's a way to try to make "impossible state impossible". 

Little disclaimer from KCD:

{% twitter 1171453307358834690 %}