Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
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 video, hereâs a brief explanation of the code you see:
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 original component 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 there to modify props. It takes in a function f that takes in some value and returns another value. The returned value is then fed into the original react component for rendering as a prop.
We can say that contramap transforms the input to the component.
Modifying the output
Modifying the output means to modify JSX. You may be thinking of a higher order component. But hereâs the problemâââ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). The function f takes some JSX and wraps the original componentâs output in it.
Safety First
Before venturing any further, we need to make the code safeâââtype safe.
Here are the basic flow types:
The important thing to observe here is that the type Component is a mapping from type Props to type Element.
Armed with types, we can now annotate our Comp functionâs return object. Please note that I am using Flowâ $Exact utility type to make the intent clear that type Box is an object with fixed keys and values.
If we really think about it, we can see that the function Comp is a mapping from type Component to type Box.
If youâre not a fan of arrow functions, you can rewrite the same thing as a function declaration. I prefer the current system because I can decouple my types from my code. IMO, thatâs much cleaner.
Writing Comp using function declaration means weâd have to integrate the types into the function definition.
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.
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:
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:
- We no longer need a separate <div /> to wrap other JSXÂ elements
- Our concat is now associative. Huzzah!
Conclusion
With the final addition of a concat method, we can take a step back to admire the beauty of our code.
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 abstractionIâ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 RefsTypically, 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 elementsOkay. This one is pretty obvious. We just wrap one component in another. This is exactly what the map function does:
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:
- https://www.youtube.com/watch?v=SfWR3dKnFIo
- https://www.youtube.com/watch?v=JZSoPZUoR58
- https://creaturephil.github.io/posts/exploring-oh-composable-world/
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.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.