Let’s Learn: Composing React Components With Ramda

How many buzzwords can we fit in a title?

Tim Roberts
Let’s Learn:
5 min readMar 1, 2017

--

The React world is in love with Higher Order Components . They let us write logic for our components once inside a component and reuse that component over and over, passing it the display data whenever we want to use it. They offer component composition. But what if instead of using higher-order components we used higher-order functions to create our final React component that we pass data to? How would that look?

First, let’s understand the problem we are trying to solve here. We have an array of post objects and we want to display a list of post titles and underneath each title the comments that are on that post. A first pass to get it up and going, to put the boxes where they need to go, looks something like this:

It’s really ugly and we can definitely do some abstraction with it. Let’s make the titles and comments into their own components so we make this function a little easier to read:

Here we have taken out the logic of how we display the titles and comments so our Posts component is only worried about getting some data as props and passing it along to the functions. Once again, this works but I think we can do better. To see how, let’s start with Titles and refactor that to use higher order functions and ramda.

compose and map are from ramda and they are key to the idea we are getting at. I’m still getting used to how to use ramda effectively so let’s dive into what the above code is doing, starting at the top.

const Title = title => (...) looks like a normal Stateless React component but instead of us pulling title off of a prop object like ({title}) , we just expect to receive a single value that we are calling title and using that to return some JSX. This difference comes into play in the next function.

const TitleList = compose(... is where we start to get a little different than before. Instead of me creating a function expecting the variable posts and me iterating over it like before, I am creating a function that describes all of the iterations on a future list. What do I mean?

The value that compose(...) returns is a function which means that TitleList is a function. How are we defining what this function does? By composing other functions together. Here we are composing the map(prop('title')) function together and the map(Title) function together. Just like compose(...) returns a function, giving the function map a single value returns a function. When I say the function map(Title), I am referring to the value that calling map with the single value Title would return. To break this up a little more, let’s name these two functions so we can see what I am saying:

Our Title function takes in a value and returns some final value. makeTitles is a function that is expecting an array. Whenever we pass makeTitles a value, it will map over it, applying the function Title to each element, creating a new array of values.

getTitle is a function that is expecting an object and whenever you give it one, it will return the value at the key title. The getTitles function expects an array and, just like makeTitles, it will apply the function given to it for each item in the array, creating a new one.

So that means our function TitleList is waiting to be given an array and it will then pass that array to getTitles ( because compose is performed bottom-top / right-left ) which returns an array of strings, which are all of the title keys from the array of objects passed in. That array gets passed to makeTitles which takes an array of strings and creates an array of React components.

How is this helpful? Because now the only bit of React code that we have to worry about is a simple h2 tag: a totally stateless component that we can test and be sure it works exactly how we need it to. Outside of the presentational layer, the part that React does really well, we are left to solve the logical layer using pure functions that we can test and be sure work exactly how we intend to.

Because these two things are decoupled, we are left with an easier way to compose functionality. This really shines when we start to refactor our Comments component.

Our first pass is us breaking out the inner iteration from the outer. Because the outer posts.map doesn’t really need to know about how makeComments works, we can just pass comments to the function and go along its merry way. Like before, let’s refactor makeComments to be functional.

Once again, we see that when the problem is solved away from the presentation of the solution, we are left with simple, dumb components that are just props in, data out. But we’re still left with Comments having a map in it and being all weird. Let’s see what we can do about that.

This is looking alot better. We are creating complex instructions using small, testable building blocks. We created a CommentWrapper component to give some housekeeping markup to CommentList ‘s return value. We also create a getComments function to point the posts data to the Comment and CommentWrapper functions.

Is there anything else we can abstract out so we can easily change out pieces later? Where is there a component that is not relying totally on input to perform its job?

Our housekeeping CommentWrapper is using a function that is outside its scope. Let’s see if we can fix that.

We make a new function called CommentWrapper, which just takes in a value, wraps it in some housekeeping markup, and displays the value inside of it. Totally dumb, totally testable. Nice and clean. Is our code 100% pure now? Not quite. Comments is basically CommentWrapper, taking in some value and putting housekeeping markup around it. I wonder how we can abstract the call to getComments to just rely on input.

Finally. Every piece of the Comments function, the final glue between the Posts component and the actual data that we want to display from it, is a small, stateless, pure function that we can modify and test in an isolated way. Just using a few ramda helper functions that are also found inlodash/fp and the bare minimum React components/JSX needed to display the data. We can use Comments like any other React component:

<Comments posts={posts} />

Recap

What we have done is taken a ‘make it pass the tests’ component and turned it into a series of small transformations using pure functions. Each piece is small, reusable, and composable and we can change any one piece as long as it expects the same input type and returns the same output type. We have created lego blocks of code that we can piece together to create a final result.

Is this the most performant? Probably not. Do I have tests? Only the cases that pass. This is not meant to be a copy/paste tutorial but rather point towards a different way of seeing React components and programming logic.

If we build our code only relying on input and only concerned with its output, we can build very robust systems on top of very simple instructions. And our worker is fantastic at doing only one thing: following simple instructions quickly.

--

--

Tim Roberts
Let’s Learn:

dev kid who likes to write in english instead of code