Resisting Higher Order and accepting Functional

So the big idea is that although React itself is declarative, vast majority of code that goes into making an app is imperative. This can be fixed (if you think it needs fixing) by mixing in more functional patterns.

Rendering data

What is react?

A JavaScript library for building user interfaces — reactjs.org

Although React is for building data view (or user interfaces), it must mingle with data, in some form, at times. And when it does, almost always the data is tightly coupled with the the interface code.

Wouldn’t it be great if we could just pack our views in a separate box and just sprinkle the data as and when needed?

A box called “Comp”

This code is from the talk “Oh Composable World!” by Brian Lonsdorf. If you haven’t seen it yet, I encourage you do so before continuing.

Oh Composable World! @ 21:40

If you haven’t seen the talk, here’s the gist of the code snippet you see above:

The Comp function takes in a react component (as the argument g) and returns an object with methods fold and contramap.

The fold method is the component originally passed to Comp. This means that calling fold is just like calling Foo or rendering <Foo />. After all, the following expressions give equivalent values.

  • <Foo fizz="buzz" />
  • Foo({ fizz: "buzz" })
  • Comp(Foo).fold({ fizz: "buzz" })

The contramap method is a bit tricky. It takes in some value and returns another value. The returned value is then fed into the original react component for rendering as a prop. Basically, contramap transforms the input of the component function, i.e., it modifies the input.

To highlight the contramap behaviour, let’s modify the code:

Now, the Heading component accepts an object — the props object — and returns some JSX. This follows react’s definition of a functional component.

Modifying the output

Modifying the output means to modify the returned JSX. You might think of a higher order component. But here’s the thing: HOCs don’t work with JSX.

Concretely, a higher-order component is a function that takes a component and returns a new component. — React docs on Higher order components

With the help of flow, if I create types for both Component and HOC, I get:

  • type Component = Props => Element;
  • type HOC = Component => Component;

It isn’t immediately obvious, but the type signature for an HOC is pretty long:
type HOC = (Props => Element) => (Props => Element);

What we want is fairly simple: Element => Element.

The map function

This is what the map function is used for. If contramap deals with a component’s input (props) then map deals with its output (JSX).

Safety First

Before venturing any further, we need to make the code safe — type safe.

Here are the basic flow types:

Equipped with these types, we can now annotate our Comp function. Please note that I am using Flow’ $Exact utility type to make the intent clear that type Box returns an object with fixed keys and values.

Using the arrow function to declare functions (like Comp) or even function expressions is as a blessing when adding type annotations since we can decouple the type annotation from the actual function. IMO, that’s much cleaner and I prefer it that way.

If we were to rewrite Comp using function declaration, we’d have to integrate the types into the function definition and add some helper types:

Note: I named my types Box and Foo because naming things is difficult. If you can suggest some good ones, do tell.

JSX Pipeline

Right now, every Box object sits in isolation from the rest of the world. They can’t even connect with each. But if they could, we’d have an extensible JSX pipeline at our hands.

The concat function

Conceptually, the concat function merges two entities of the same type. We should also add a concat method to our Box type.

With the help of our new friend, we can now join multiple Boxes together.

Nasty concat

It works nicely but there’s a small hiccup. Everytime concat is called, it appends a new <div /> into the markup. And that’s nasty.

An array equivalent would look something like:
[1].concat([2]) === [1, [2]]

But we know that’s not how concat works on arrays. It works like:
[1].concat([2]) === [1, 2]

This means that array’s concat method is associative:
[1].concat([2].concat(3)) === [1].concat([2]).concat(3) === [1, 2, 3]

But our concat is not associative:

Nice concat

The easy way out is to wrap the result of concat in an array, since arrays already have a nice and friendly concat method, and also because React 16 added support for rendering an array of JSX elements. But doing so means rewriting the Comp function. Besides, it’s already been covered in “Deconstructing the React Component” by Jack Hsu.

Instead, we will use the <Fragment /> component. If you’re feeling adventurous, you can use the updated syntax for fragments.

With such a minor update to the code, we’ve solved two problems:

  1. We no longer need a separate <div /> to wrap other JSX elements
  2. Our concat is now associative. Huzzah!

But why?

Without going any further than this, it’s best if we talk about the merits of our current system. In the article “React Higher Order Components in depth”, Fran Guijarro has outlined numerous use cases for HOCs and split them up in two classes of implementation — Props Proxy and Inheritance Inversion. We’ll look at each of them and see what we can do about them.

Props Proxy

This is the more conventional usage of an HOC and it allows:

  • Props manipulation
  • State abstraction
  • Accessing the instance via Refs
  • Wrapping the WrappedComponent with other elements

Props manipulation and State abstraction
I’ll have you know that there’s yet another pattern that deals with this specific usage of HOCs. It’s called Render Props and you can read more about it in my other article “Exploring Render Props”.

Let’s look at some sample code that uses a HOC to implement both usages:

Before we can make the code functional, we need to convert it to the render props pattern.

We can now functional-ise the code. First, we pack <Num /> and <Foo /> in separate Boxes. Since Num is a class, we also need a function to convert ES6 classes into functions. Luckily, Brian Lonsdorf got us covered.

And now, we define our <App /> in terms of Boxes.

And that’s it. Two HOC uses down, a few more to go.

Accessing the instance via Refs
Typically, this is how we use refs in HOCs:

Which is, under the hood, just a nested component:

And the functional equivalent is:

Wrapping the WrappedComponent with other elements
Okay. This one is pretty obvious. We just wrap one component in another. This is exactly what the map function does:

Inheritance Inversion

We have covered the entirety of what an HOC can do by acting like a component pass through. If you thought that was interesting, just check out how to implement II — it’s bound to confuse you. An HOC that returns a new component that extends WrappedComponent.

This is the one thing that we can’t do with with Boxes. We cannot have inheritance amongst functions and functions are a major participant in our functional pattern. And this makes me sad 😔

The End

… for now. I hope you found this bit interesting and possibly even helpful. Now we can explore data flow in our functional react pattern. But that’s an affair for another time.

The code for this post is on repl.it. If you want more, here’s a list for you:


Resisting Higher Order and accepting Functional was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: